Thứ năm, 24/01/2019 | 00:00 GMT+7

Bias-Variance để học tăng cường sâu: Cách xây dựng một Bot cho Atari với OpenAI Gym

Học củng cố là một trường con trong lý thuyết điều khiển, liên quan đến việc điều khiển các hệ thống thay đổi theo thời gian và bao gồm rộng rãi các ứng dụng như ô tô tự lái, robot và bot cho trò chơi. Trong suốt hướng dẫn này, bạn sẽ sử dụng phương pháp học củng cố để xây dựng một bot cho trò chơi điện tử Atari. Bot này không được cấp quyền truy cập vào thông tin nội bộ về trò chơi. Thay vào đó, nó chỉ được cấp quyền truy cập vào màn hình hiển thị của trò chơi và phần thưởng cho màn hình đó, nghĩa là nó chỉ có thể nhìn thấy những gì người chơi sẽ thấy.

Trong học máy, bot chính thức được biết đến như một tác nhân . Trong trường hợp của hướng dẫn này, tác nhân là một “người chơi” trong hệ thống hoạt động theo chức năng ra quyết định, được gọi là chính sách . Mục tiêu chính là phát triển các đại lý mạnh bằng cách trang bị cho họ các policy mạnh mẽ. Nói cách khác, mục đích của ta là phát triển các bot thông minh bằng cách trang bị cho chúng khả năng ra quyết định mạnh mẽ.

Bạn sẽ bắt đầu hướng dẫn này bằng cách đào tạo một nhân viên học tăng cường cơ bản thực hiện các hành động ngẫu nhiên khi chơi Space Invaders, trò chơi arcade cổ điển của Atari, sẽ là cơ sở để bạn so sánh. Sau đây, bạn sẽ khám phá một số kỹ thuật khác - bao gồm Q-learning , deep Q-learning và các ô vuông nhỏ nhất - trong khi xây dựng các đặc vụ chơi Space Invaders và Frozen Lake, một môi trường trò chơi đơn giản có trong Phòng tập thể dục , một bộ công cụ học tập tăng cường do OpenAI phát hành . Theo hướng dẫn này, bạn sẽ hiểu được các khái niệm cơ bản chi phối sự lựa chọn của một người về độ phức tạp của mô hình trong học máy.

Yêu cầu

Để hoàn thành hướng dẫn này, bạn cần :

Ngoài ra, nếu bạn đang sử dụng máy local , bạn có thể cài đặt Python 3 và cài đặt môi trường lập trình local bằng cách đọc hướng dẫn thích hợp cho hệ điều hành của bạn qua Chuỗi cài đặt và cài đặt Python của ta .

Bước 1 - Tạo dự án và cài đặt phụ thuộc

Để cài đặt môi trường phát triển cho bot của bạn, bạn phải download chính trò chơi và các thư viện cần thiết để tính toán.

Bắt đầu bằng cách tạo một không gian làm việc cho dự án này có tên AtariBot :

  • mkdir ~/AtariBot

Điều hướng đến folder AtariBot mới:

  • cd ~/AtariBot

Sau đó, tạo một môi trường ảo mới cho dự án. Bạn có thể đặt tên cho môi trường ảo này bạn muốn ; ở đây, ta sẽ đặt tên nó là ataribot :

  • python3 -m venv ataribot

Kích hoạt môi trường của bạn:

  • source ataribot/bin/activate

Trên Ubuntu, kể từ version 16.04, OpenCV yêu cầu cài đặt thêm một số gói để hoạt động. Chúng bao gồm CMake - một ứng dụng quản lý quy trình xây dựng phần mềm - cũng như trình quản lý phiên, các tiện ích mở rộng khác và thành phần hình ảnh kỹ thuật số. Chạy lệnh sau để cài đặt các gói này:

  • sudo apt-get install -y cmake libsm6 libxext6 libxrender-dev libz-dev

LƯU Ý: Nếu bạn đang làm theo hướng dẫn này trên máy local chạy MacOS, thì phần mềm bổ sung duy nhất bạn cần cài đặt là CMake. Cài đặt nó bằng Homebrew (bạn sẽ cài đặt nó nếu bạn làm theo hướng dẫn yêu cầu về MacOS ) bằng lệnh :

  • brew install cmake

Tiếp theo, sử dụng pip để cài đặt gói wheel , việc thực hiện tham khảo của tiêu chuẩn đóng gói bánh xe. Một thư viện Python, gói này đóng role như một phần mở rộng để xây dựng các bánh xe và bao gồm một công cụ dòng lệnh để làm việc với các file .whl :

  • python -m pip install wheel

Ngoài wheel , bạn cần cài đặt các gói sau:

  • Phòng tập thể dục , một thư viện Python cung cấp nhiều trò chơi khác nhau để nghiên cứu, cũng như tất cả các phụ thuộc cho các trò chơi Atari. Được phát triển bởi OpenAI , Gym cung cấp các điểm chuẩn công khai cho mỗi trò chơi để hiệu suất của các tác nhân và thuật toán khác nhau có thể được đánh giá / thống nhất.
  • Tensorflow , một thư viện học sâu. Thư viện này cung cấp cho ta khả năng chạy tính toán hiệu quả hơn. Cụ thể, nó thực hiện điều này bằng cách xây dựng các hàm toán học bằng cách sử dụng các bản tóm tắt của Tensorflow chạy riêng trên GPU của bạn.
  • OpenCV , thư viện thị giác máy tính đã đề cập trước đây.
  • SciPy , một thư viện máy tính khoa học cung cấp các thuật toán tối ưu hóa hiệu quả.
  • NumPy , một thư viện đại số tuyến tính.

Cài đặt từng gói này bằng lệnh sau. Lưu ý lệnh này chỉ định version của từng gói để cài đặt:

  • python -m pip install gym==0.9.5 tensorflow==1.5.0 tensorpack==0.8.0 numpy==1.14.0 scipy==1.1.0 opencv-python==3.4.1.15

Sau đó, sử dụng pip để cài đặt môi trường Atari của Phòng tập thể dục, bao gồm nhiều trò chơi điện tử Atari, bao gồm cả Space Invaders:

  • python -m pip install gym[atari]

Nếu quá trình cài đặt gói gym[atari] của bạn thành công, kết quả kết quả của bạn sẽ kết thúc như sau:

Output
Installing collected packages: atari-py, Pillow, PyOpenGL Successfully installed Pillow-5.4.1 PyOpenGL-3.1.0 atari-py-0.1.7

Với những phần phụ thuộc này được cài đặt, bạn đã sẵn sàng để tiếp tục và xây dựng một tác nhân phát ngẫu nhiên để làm đường cơ sở cho bạn so sánh.

Bước 2 - Tạo tác nhân ngẫu nhiên cơ bản với phòng tập thể dục

Bây giờ phần mềm cần thiết đã có trên server của bạn, bạn sẽ cài đặt một nhân viên để chơi version đơn giản hóa của trò chơi Atari cổ điển, Space Invaders. Đối với bất kỳ thử nghiệm nào, bạn cần phải có được đường cơ sở để giúp bạn hiểu mô hình của bạn hoạt động tốt như thế nào. Bởi vì tác nhân này thực hiện các hành động ngẫu nhiên ở mỗi khung, ta sẽ gọi nó là tác nhân cơ bản, ngẫu nhiên của ta . Trong trường hợp này, bạn sẽ so sánh với tác nhân cơ sở này để hiểu tác nhân của bạn hoạt động tốt như thế nào trong các bước sau.

Với Gym, bạn duy trì vòng lặp trò chơi của riêng mình. Điều này nghĩa là bạn xử lý mọi bước trong quá trình thực hiện trò chơi: tại mỗi bước, bạn cung cấp cho gym một hành động mới và yêu cầu gym cho trạng thái trò chơi . Trong hướng dẫn này, trạng thái trò chơi là giao diện của trò chơi tại một bước thời gian nhất định và chính xác là những gì bạn sẽ thấy nếu bạn đang chơi trò chơi.

Sử dụng editor bạn muốn , tạo một file Python có tên bot_2_random.py . Ở đây, ta sẽ sử dụng nano :

  • nano bot_2_random.py

Lưu ý: Trong suốt hướng dẫn này, tên của các bot được căn chỉnh theo số Bước mà chúng xuất hiện, thay vì thứ tự xuất hiện. Do đó, bot này được đặt tên là bot_2_random.py thay vì bot_1_random.py .

Bắt đầu tập lệnh này bằng cách thêm các dòng được đánh dấu sau. Những dòng này bao gồm một khối comment giải thích những gì kịch bản này sẽ làm và hai import báo cáo rằng sẽ nhập khẩu các gói kịch bản này cuối cùng cần để chức năng:

/AtariBot/bot_2_random.py
""" Bot 2 -- Make a random, baseline agent for the SpaceInvaders game. """  import gym import random 

Thêm một chức năng main . Trong chức năng này, tạo môi trường trò chơi - SpaceInvaders-v0 - và sau đó khởi tạo trò chơi bằng env.reset :

/AtariBot/bot_2_random.py
. . . import gym import random  def main():     env = gym.make('SpaceInvaders-v0')     env.reset()  

Tiếp theo, thêm một hàm env.step . Hàm này có thể trả về các loại giá trị sau:

  • state : state mới của trò chơi, sau khi áp dụng hành động được cung cấp.
  • reward : Sự gia tăng điểm số mà bang nhận được. Ví dụ, đây có thể là khi một viên đạn tiêu diệt một người ngoài hành tinh và điểm số tăng lên 50 điểm. Sau đó, reward = 50 . Khi chơi bất kỳ trò chơi dựa trên điểm số nào, mục tiêu của người chơi là tối đa hóa điểm số. Điều này đồng nghĩa với việc tối đa hóa tổng phần thưởng.
  • done : Cho dù tập đã kết thúc hay chưa, thường xảy ra khi người chơi mất hết mạng.
  • info : info không liên quan mà bạn sẽ bỏ qua một bên.

Bạn sẽ sử dụng reward để tính tổng phần thưởng của bạn . Bạn cũng sẽ sử dụng done để xác định khi chết cầu thủ, đó sẽ là khi done lợi nhuận True .

Thêm vòng lặp trò chơi sau, hướng dẫn trò chơi lặp lại cho đến khi người chơi chết:

/AtariBot/bot_2_random.py
. . . def main():     env = gym.make('SpaceInvaders-v0')     env.reset()      episode_reward = 0     while True:         action = env.action_space.sample()         _, reward, done, _ = env.step(action)         episode_reward += reward         if done:             print('Reward: %s' % episode_reward)             break  

Cuối cùng, chạy chức năng main . Bao gồm kiểm tra __name__ đảm bảo rằng main chỉ chạy khi bạn gọi nó trực tiếp bằng python bot_2_random.py . Nếu bạn không thêm dấu kiểm if , main sẽ luôn được kích hoạt khi file Python được thực thi, ngay cả khi bạn nhập file . Do đó, bạn nên đặt mã trong một hàm main , chỉ được thực thi khi __name__ == '__main__' .

/AtariBot/bot_2_random.py
. . . def main():     . . .     if done:         print('Reward %s' % episode_reward)         break  if __name__ == '__main__':     main() 

Lưu file và thoát khỏi editor . Nếu bạn đang sử dụng nano , hãy làm như vậy bằng cách nhấn CTRL+X , Y , sau đó ENTER . Sau đó, chạy tập lệnh của bạn bằng lệnh :

  • python bot_2_random.py

Chương trình của bạn sẽ xuất ra một số, tương tự như sau. Lưu ý mỗi lần chạy file , bạn sẽ nhận được một kết quả khác nhau:

Output
Making new env: SpaceInvaders-v0 Reward: 210.0

Những kết quả ngẫu nhiên này đưa ra một vấn đề. Để tạo ra công việc mà các nhà nghiên cứu và các nhà thực hành khác có thể hưởng lợi, các kết quả và thử nghiệm của bạn phải được tái tạo. Để sửa lỗi này, hãy mở lại file script:

  • nano bot_2_random.py

Sau khi import random , hãy thêm random.seed(0) . Sau env = gym.make('SpaceInvaders-v0') , thêm env.seed(0) . Cùng với nhau, những dòng này “gieo mầm” cho môi trường với một điểm xuất phát nhất quán, đảm bảo các kết quả sẽ luôn được lặp lại. Tệp cuối cùng của bạn sẽ khớp chính xác như sau:

/AtariBot/bot_2_random.py
""" Bot 2 -- Make a random, baseline agent for the SpaceInvaders game. """  import gym import random random.seed(0)   def main():     env = gym.make('SpaceInvaders-v0')     env.seed(0)      env.reset()     episode_reward = 0     while True:         action = env.action_space.sample()         _, reward, done, _ = env.step(action)         episode_reward += reward         if done:             print('Reward: %s' % episode_reward)             break   if __name__ == '__main__':     main() 

Lưu file và đóng editor , sau đó chạy tập lệnh bằng lệnh nội dung sau vào terminal của bạn:

  • python bot_2_random.py

Điều này sẽ tạo ra phần thưởng sau, chính xác:

Output
Making new env: SpaceInvaders-v0 Reward: 555.0

Đây là bot đầu tiên của bạn, mặc dù nó khá kém thông minh vì nó không tính đến môi trường xung quanh khi đưa ra quyết định. Để có một ước tính tin cậy hơn về hiệu suất của bot của bạn, bạn có thể yêu cầu tác nhân chạy nhiều tập cùng một lúc, báo cáo phần thưởng được tính trung bình qua nhiều tập. Để cấu hình điều này, trước tiên hãy mở lại file :

  • nano bot_2_random.py

Sau random.seed(0) , hãy thêm dòng được đánh dấu sau để yêu cầu nhân viên chơi trò chơi trong 10 tập:

/AtariBot/bot_2_random.py
. . . random.seed(0)  num_episodes = 10 . . . 

Ngay sau env.seed(0) , hãy bắt đầu danh sách phần thưởng mới:

/AtariBot/bot_2_random.py
. . .     env.seed(0)     rewards = [] . . . 

env.reset() tất cả mã từ env.reset() đến cuối main() trong một vòng lặp for , lặp lại num_episodes lần num_episodes . Đảm bảo thụt lề mỗi dòng từ env.reset() để break bởi bốn dấu cách:

/AtariBot/bot_2_random.py
. . . def main():     env = gym.make('SpaceInvaders-v0')     env.seed(0)     rewards = []      for _ in range(num_episodes):         env.reset()         episode_reward = 0          while True:             ... 

Ngay trước break , hiện tại là dòng cuối cùng của vòng lặp trò chơi chính, hãy thêm phần thưởng của tập hiện tại vào danh sách tất cả phần thưởng:

/AtariBot/bot_2_random.py
. . .         if done:             print('Reward: %s' % episode_reward)             rewards.append(episode_reward)             break . . . 

Vào cuối chức năng main , báo cáo phần thưởng trung bình:

/AtariBot/bot_2_random.py
. . . def main():     ...             print('Reward: %s' % episode_reward)             break     print('Average reward: %.2f' % (sum(rewards) / len(rewards)))     . . . 

Tệp của bạn bây giờ sẽ căn chỉnh với những thứ sau. Xin lưu ý khối mã sau đây bao gồm một số comment để làm rõ các phần chính của tập lệnh:

/AtariBot/bot_2_random.py
""" Bot 2 -- Make a random, baseline agent for the SpaceInvaders game. """  import gym import random random.seed(0)  # make results reproducible  num_episodes = 10   def main():     env = gym.make('SpaceInvaders-v0')  # create the game     env.seed(0)  # make results reproducible     rewards = []      for _ in range(num_episodes):         env.reset()         episode_reward = 0         while True:             action = env.action_space.sample()             _, reward, done, _ = env.step(action)  # random action             episode_reward += reward             if done:                 print('Reward: %d' % episode_reward)                 rewards.append(episode_reward)                 break     print('Average reward: %.2f' % (sum(rewards) / len(rewards)))   if __name__ == '__main__':     main() 

Lưu file , thoát khỏi editor và chạy tập lệnh:

  • python bot_2_random.py

Điều này sẽ in chính xác phần thưởng trung bình sau:

Output
Making new env: SpaceInvaders-v0 . . . Average reward: 163.50

Bây giờ ta có một ước tính tin cậy hơn về điểm cơ bản để đánh bại. Tuy nhiên, để tạo ra một tác nhân cấp cao, bạn cần phải hiểu cơ chế học tập củng cố. Làm thế nào người ta có thể biến khái niệm trừu tượng về “ra quyết định” cụ thể hơn?

Hiểu học tập củng cố

Trong bất kỳ trò chơi nào, mục tiêu của người chơi là tối đa hóa số điểm của họ. Trong hướng dẫn này, điểm của người chơi được gọi là phần thưởng của nó. Để tối đa hóa phần thưởng của họ, người chơi phải có khả năng tinh chỉnh khả năng ra quyết định của bạn . Về mặt hình thức, quyết định là quá trình xem xét trò chơi, hoặc quan sát trạng thái của trò chơi và chọn một hành động. Chức năng ra quyết định của ta được gọi là chính sách ; một policy chấp nhận một trạng thái làm đầu vào và “quyết định” một hành động:

policy: state -> action 

Để xây dựng một hàm như vậy, ta sẽ bắt đầu với một bộ thuật toán cụ thể trong học tăng cường được gọi là thuật toán Q-learning . Để minh họa những điều này, hãy xem xét trạng thái ban đầu của một trò chơi, mà ta sẽ gọi là trạng state0 : tàu vũ trụ của bạn và người ngoài hành tinh đều ở vị trí bắt đầu của họ. Sau đó, giả sử ta có quyền truy cập vào một "bảng Q" kỳ diệu cho ta biết mỗi hành động sẽ kiếm được bao nhiêu phần thưởng:

tiểu bang hoạt động phần thưởng
bang0 bắn 10
bang0 đúng 3
bang0 trái 3

Hành động shoot sẽ tối đa hóa phần thưởng của bạn, vì nó dẫn đến phần thưởng có giá trị cao nhất: 10. Như bạn thấy , bảng Q cung cấp một cách dễ dàng để đưa ra quyết định, dựa trên trạng thái được quan sát:

policy: state -> look at Q-table, pick action with greatest reward 

Tuy nhiên, hầu hết các trò chơi có quá nhiều trạng thái để liệt kê trong một bảng. Trong những trường hợp như vậy, Q-learning agent sẽ học hàm Q thay vì bảng Q. Ta sử dụng hàm Q này tương tự như cách ta sử dụng bảng Q trước đây. Viết lại các mục trong bảng dưới dạng các hàm cho ta những điều sau:

Q(state0, shoot) = 10 Q(state0, right) = 3 Q(state0, left) = 3 

Với một trạng thái cụ thể, ta dễ dàng đưa ra quyết định: ta chỉ cần xem xét từng hành động có thể xảy ra và phần thưởng của nó, sau đó thực hiện hành động tương ứng với phần thưởng mong đợi cao nhất. Cải cách policy trước đó một cách chính thức hơn, ta có:

policy: state -> argmax_{action} Q(state, action) 

Điều này đáp ứng các yêu cầu của một chức năng ra quyết định: với một trạng thái trong trò chơi, nó sẽ quyết định một hành động. Tuy nhiên, giải pháp này phụ thuộc vào việc biết Q(state, action) cho mọi trạng thái và hành động. Để ước tính Q(state, action) , hãy xem xét những điều sau:

  1. Với nhiều quan sát về trạng thái, hành động và phần thưởng của một tác nhân, người ta có thể ước tính phần thưởng cho mọi trạng thái và hành động bằng cách lấy giá trị trung bình.
  2. Space Invaders là một trò chơi có phần thưởng bị trì hoãn: người chơi được thưởng khi người ngoài hành tinh bị nổ tung chứ không phải khi người chơi bắn. Tuy nhiên, người chơi thực hiện một hành động bằng cách bắn mới là động lực thực sự cho phần thưởng. Bằng cách nào đó, hàm Q phải ấn định (state0, shoot) một phần thưởng tích cực.

Hai hiểu biết này được hệ thống hóa trong các phương trình sau:

Q(state, action) = (1 - learning_rate) * Q(state, action) + learning_rate * Q_target Q_target = reward + discount_factor * max_{action'} Q(state', action') 

Các phương trình này sử dụng các định nghĩa sau:

  • state : trạng thái ở bước thời gian hiện tại
  • action : hành động được thực hiện ở bước thời gian hiện tại
  • reward : phần thưởng cho bước thời gian hiện tại
  • state' : trạng thái mới cho bước thời gian tiếp theo, cho rằng ta đã hành động a
  • action' : tất cả các hành động có thể
  • learning_rate : tỷ lệ học tập
  • discount_factor : hệ số chiết khấu, mức độ phần thưởng “giảm đi” khi ta tuyên truyền

Để có lời giải thích đầy đủ về hai phương trình này, hãy xem bài viết này về Hiểu Q-Learning .

Với sự hiểu biết về học tập củng cố này, tất cả những gì còn lại là thực sự chạy trò chơi và nhận các ước tính giá trị Q này cho một policy mới.

Bước 3 - Tạo Q-learning Agent đơn giản cho Frozen Lake

Đến đây bạn đã có tác nhân cơ bản, bạn có thể bắt đầu tạo tác nhân mới và so sánh chúng với tác nhân root . Trong bước này, bạn sẽ tạo một tác nhân sử dụng Q-learning , một kỹ thuật học tăng cường được sử dụng để dạy một tác nhân thực hiện hành động nào ở một trạng thái nhất định. Đặc vụ này sẽ chơi một trò chơi mới, FrozenLake . Cài đặt cho trò chơi này được mô tả như sau trên trang web của Phòng tập thể dục:

Mùa đông đến rồi. Bạn và bạn bè của bạn đang tung một chiếc đĩa bay ở công viên khi bạn thực hiện một cú ném hoang dã khiến chiếc đĩa quay giữa hồ bị rơi ra ngoài. Nước hầu như đóng băng, nhưng có một vài lỗ thủng nơi băng đã tan chảy. Nếu bạn bước vào một trong những lỗ đó, bạn sẽ rơi xuống nước đóng băng. Đến đây, quốc tế đang khan hiếm trò chơi ném đĩa, vì vậy bạn hoàn toàn bắt buộc phải chuyển qua hồ và lấy đĩa. Tuy nhiên, băng trơn nên không phải lúc nào bạn cũng di chuyển theo hướng bạn định.

Bề mặt được mô tả bằng cách sử dụng lưới như sau:

SFFF       (S: starting point, safe) FHFH       (F: frozen surface, safe) FFFH       (H: hole, fall to your doom) HFFG       (G: goal, where the frisbee is located) 

Người chơi bắt đầu ở phía trên bên trái, ký hiệu là S và di chuyển đến mục tiêu ở phía dưới bên phải, ký hiệu là G Các hành động có sẵn là sang phải , sang trái , lênxuống , và đạt được mục tiêu dẫn đến điểm 1. Có một số lỗ, ký hiệu là H , và rơi vào một lỗ ngay lập tức dẫn đến điểm 0.

Trong phần này, bạn sẽ triển khai một Q-learning agent đơn giản. Sử dụng những gì bạn đã học trước đây, bạn sẽ tạo ra một đại lý giao dịch giữa thăm dòkhai thác . Trong bối cảnh này, thăm dò nghĩa là tác nhân hành động ngẫu nhiên và khai thác nghĩa là nó sử dụng các giá trị Q của nó để chọn những gì nó cho là hành động tối ưu. Bạn cũng cần tạo một bảng để chứa các giá trị Q, cập nhật nó tăng dần khi tác nhân hành động và học hỏi.

Tạo một bản sao tập lệnh của bạn từ Bước 2:

  • cp bot_2_random.py bot_3_q_table.py

Sau đó, mở file mới này để chỉnh sửa:

  • nano bot_3_q_table.py

Bắt đầu bằng cách cập nhật comment ở đầu file mô tả mục đích của tập lệnh. Bởi vì đây chỉ là một comment , thay đổi này không cần thiết để tập lệnh hoạt động bình thường, nhưng nó có thể hữu ích để theo dõi những gì tập lệnh thực hiện:

/AtariBot/bot_3_q_table.py
""" Bot 3 -- Build simple q-learning agent for FrozenLake """  . . . 

Trước khi bạn thực hiện các sửa đổi chức năng đối với tập lệnh, bạn cần nhập numpy cho các tiện ích đại số tuyến tính của nó. Ngay bên dưới import gym , thêm dòng được đánh dấu:

/AtariBot/bot_3_q_table.py
""" Bot 3 -- Build simple q-learning agent for FrozenLake """  import gym import numpy as np import random random.seed(0)  # make results reproducible . . . 

Bên dưới random.seed(0) , thêm một hạt giống cho numpy :

/AtariBot/bot_3_q_table.py
. . . import random random.seed(0)  # make results reproducible np.random.seed(0) . . . 

Tiếp theo, làm cho các trạng thái trò chơi có thể truy cập được. Cập nhật dòng env.reset() để nói như sau, dòng này lưu trữ trạng thái ban đầu của trò chơi ở state biến:

/AtariBot/bot_3_q_table.py
. . .     for _ in range(num_episodes):         state = env.reset()         . . . 

Cập nhật dòng env.step(...) để nói như sau, dòng này lưu trữ trạng thái tiếp theo, state2 . Bạn cần cả state hiện tại và state tiếp theo - state2 - để cập nhật hàm Q.

/AtariBot/bot_3_q_table.py
        . . .         while True:             action = env.action_space.sample()             state2, reward, done, _ = env.step(action)             . . . 

Sau episode_reward += reward , hãy thêm một dòng cập nhật state biến. Điều này giữ cho state biến được cập nhật cho lần lặp tiếp theo, vì bạn sẽ mong đợi state phản ánh trạng thái hiện tại:

/AtariBot/bot_3_q_table.py
. . .         while True:             . . .             episode_reward += reward             state = state2             if done:                 . . . 

Trong khối if done , xóa print sẽ in phần thưởng cho mỗi tập. Thay vào đó, bạn sẽ xuất ra phần thưởng trung bình qua nhiều tập. Khối if done sau đó sẽ trông như thế này:

/AtariBot/bot_3_q_table.py
            . . .             if done:                 rewards.append(episode_reward)                 break                 . . . 

Sau những sửa đổi này, vòng lặp trò chơi của bạn sẽ khớp như sau:

/AtariBot/bot_3_q_table.py
. . .     for _ in range(num_episodes):         state = env.reset()         episode_reward = 0         while True:             action = env.action_space.sample()             state2, reward, done, _ = env.step(action)             episode_reward += reward             state = state2             if done:                 rewards.append(episode_reward))                 break                 . . . 

Tiếp theo, thêm khả năng cho đại lý đánh đổi giữa thăm dò và khai thác. Ngay trước vòng lặp trò chơi chính của bạn (bắt đầu bằng for... ), hãy tạo bảng giá trị Q:

/AtariBot/bot_3_q_table.py
. . .     Q = np.zeros((env.observation_space.n, env.action_space.n))     for _ in range(num_episodes):       . . . 

Sau đó, viết lại vòng lặp for để hiển thị số tập:

/AtariBot/bot_3_q_table.py
. . .     Q = np.zeros((env.observation_space.n, env.action_space.n))     for episode in range(1, num_episodes + 1):       . . . 

Inside the while True: vòng lặp trò chơi bên trong, tạo ra noise . Nhiễu , hoặc dữ liệu ngẫu nhiên, vô nghĩa, đôi khi được đưa vào khi đào tạo mạng nơron sâu vì nó có thể cải thiện cả hiệu suất và độ chính xác của mô hình. Lưu ý nhiễu càng cao thì các giá trị trong Q[state, :] . Kết quả là, tiếng ồn càng cao thì khả năng tác nhân hành động độc lập với kiến thức về trò chơi càng cao. Nói cách khác, tiếng ồn cao hơn khuyến khích tác nhân khám phá các hành động ngẫu nhiên:

/AtariBot/bot_3_q_table.py
        . . .         while True:             noise = np.random.random((1, env.action_space.n)) / (episode**2.)             action = env.action_space.sample()             . . . 

Lưu ý khi episodes tăng lên, lượng nhiễu giảm theo bậc hai: theo thời gian, tác nhân khám phá ngày càng ít hơn vì nó có thể tin tưởng vào đánh giá của chính mình về phần thưởng của trò chơi và bắt đầu khai thác kiến thức của bạn .

Cập nhật dòng action để nhân viên của bạn chọn các hành động theo bảng giá trị Q, với một số khám phá được tích hợp sẵn:

/AtariBot/bot_3_q_table.py
            . . .             noise = np.random.random((1, env.action_space.n)) / (episode**2.)             action = np.argmax(Q[state, :] + noise)             state2, reward, done, _ = env.step(action)             . . . 

Sau đó, vòng lặp trò chơi chính của bạn sẽ khớp như sau:

/AtariBot/bot_3_q_table.py
. . .     Q = np.zeros((env.observation_space.n, env.action_space.n))     for episode in range(1, num_episodes + 1):         state = env.reset()         episode_reward = 0         while True:             noise = np.random.random((1, env.action_space.n)) / (episode**2.)             action = np.argmax(Q[state, :] + noise)             state2, reward, done, _ = env.step(action)             episode_reward += reward             state = state2             if done:                 rewards.append(episode_reward)                 break                 . . . 

Tiếp theo, bạn sẽ cập nhật bảng giá trị Q của bạn bằng cách sử dụng phương trình cập nhật Bellman , một phương trình được sử dụng rộng rãi trong học máy để tìm ra policy tối ưu trong một môi trường nhất định.

Phương trình Bellman kết hợp hai ý tưởng có liên quan nhiều đến dự án này. Đầu tiên, thực hiện một hành động cụ thể từ một trạng thái cụ thể nhiều lần sẽ dẫn đến một ước tính tốt cho giá trị Q liên quan đến trạng thái và hành động đó. Để làm điều này, bạn sẽ tăng số tập mà bot này phải chơi qua để trả về ước tính giá trị Q mạnh hơn. Thứ hai, phần thưởng phải lan truyền theo thời gian, để hành động ban đầu được gán một phần thưởng khác không. Ý tưởng này rõ ràng nhất trong các trò chơi có phần thưởng bị trì hoãn; ví dụ, trong Space Invaders, người chơi được thưởng khi người ngoài hành tinh bị nổ tung chứ không phải khi người chơi bắn. Tuy nhiên, người chơi bắn súng mới là động lực thực sự để nhận phần thưởng. Tương tự như vậy, hàm Q phải ấn định ( state0 , shoot ) một phần thưởng tích cực.

Đầu tiên, hãy cập nhật num_episodes bằng 4000:

/AtariBot/bot_3_q_table.py
. . . np.random.seed(0)  num_episodes = 4000 . . . 

Sau đó, thêm các siêu tham số cần thiết vào đầu file dưới dạng thêm hai biến:

/AtariBot/bot_3_q_table.py
. . . num_episodes = 4000 discount_factor = 0.8 learning_rate = 0.9 . . . 

Tính giá trị Q mục tiêu mới, ngay sau dòng chứa env.step(...) :

/AtariBot/bot_3_q_table.py
            . . .             state2, reward, done, _ = env.step(action)             Qtarget = reward + discount_factor * np.max(Q[state2, :])             episode_reward += reward             . . . 

Trên dòng ngay sau Qtarget tiêu Q, hãy cập nhật bảng giá trị Q bằng cách sử dụng trung bình có trọng số của các giá trị Q cũ và mới:

/AtariBot/bot_3_q_table.py
            . . .             Qtarget = reward + discount_factor * np.max(Q[state2, :])             Q[state, action] = (1-learning_rate) * Q[state, action] + learning_rate * Qtarget             episode_reward += reward             . . . 

Kiểm tra đảm bảo rằng vòng lặp trò chơi chính của bạn hiện trùng với những điều sau:

/AtariBot/bot_3_q_table.py
. . .     Q = np.zeros((env.observation_space.n, env.action_space.n))     for episode in range(1, num_episodes + 1):         state = env.reset()         episode_reward = 0         while True:             noise = np.random.random((1, env.action_space.n)) / (episode**2.)             action = np.argmax(Q[state, :] + noise)             state2, reward, done, _ = env.step(action)             Qtarget = reward + discount_factor * np.max(Q[state2, :])             Q[state, action] = (1-learning_rate) * Q[state, action] + learning_rate * Qtarget             episode_reward += reward             state = state2             if done:                 rewards.append(episode_reward)                 break                 . . . 

Logic của ta để đào tạo đại lý hiện đã hoàn tất. Tất cả những gì còn lại là thêm cơ chế báo cáo.

Mặc dù Python không thực thi kiểm tra kiểu nghiêm ngặt, hãy thêm các kiểu vào khai báo hàm của bạn đảm bảo tính sạch sẽ. Ở đầu file , trước dòng đầu tiên đọc import gym nhập, hãy nhập loại List :

/AtariBot/bot_3_q_table.py
. . . from typing import List import gym . . . 

Ngay sau khi learning_rate = 0.9 , bên ngoài hàm main , hãy khai báo khoảng thời gian và định dạng cho các báo cáo:

/AtariBot/bot_3_q_table.py
. . . learning_rate = 0.9 report_interval = 500 report = '100-ep Average: %.2f . Best 100-ep Average: %.2f . Average: %.2f ' \          '(Episode %d)'  def main():   . . . 

Trước hàm main , hãy thêm một hàm mới sẽ điền chuỗi report này, sử dụng danh sách tất cả các phần thưởng:

/AtariBot/bot_3_q_table.py
. . . report = '100-ep Average: %.2f . Best 100-ep Average: %.2f . Average: %.2f ' \          '(Episode %d)'  def print_report(rewards: List, episode: int):     """Print rewards report for current episode     - Average for last 100 episodes     - Best 100-episode average across all time     - Average for all episodes across time     """     print(report % (         np.mean(rewards[-100:]),         max([np.mean(rewards[i:i+100]) for i in range(len(rewards) - 100)]),         np.mean(rewards),         episode))   def main():   . . . 

Thay đổi trò chơi thành FrozenLake thay vì SpaceInvaders :

/AtariBot/bot_3_q_table.py
. . . def main():     env = gym.make('FrozenLake-v0')  # create the game     . . . 

Sau phần rewards.append(...) , in phần thưởng trung bình trong 100 tập qua và in phần thưởng trung bình trên tất cả các tập:

/AtariBot/bot_3_q_table.py
            . . .             if done:                 rewards.append(episode_reward)                 if episode % report_interval == 0:                     print_report(rewards, episode)                 . . . 

Ở cuối hàm main() , hãy báo cáo cả hai giá trị trung bình . Thực hiện việc này bằng cách thay thế dòng có nội dung print('Average reward: %.2f' % (sum(rewards) / len(rewards))) bằng dòng được đánh dấu sau:

/AtariBot/bot_3_q_table.py
. . . def main():     ...                 break     print_report(rewards, -1) . . . 

Cuối cùng, bạn đã hoàn thành Q-learning agent của bạn . Kiểm tra xem tập lệnh của bạn có phù hợp với những điều sau đây không:

/AtariBot/bot_3_q_table.py
""" Bot 3 -- Build simple q-learning agent for FrozenLake """  from typing import List import gym import numpy as np import random random.seed(0)  # make results reproducible np.random.seed(0)  # make results reproducible  num_episodes = 4000 discount_factor = 0.8 learning_rate = 0.9 report_interval = 500 report = '100-ep Average: %.2f . Best 100-ep Average: %.2f . Average: %.2f ' \          '(Episode %d)'   def print_report(rewards: List, episode: int):     """Print rewards report for current episode     - Average for last 100 episodes     - Best 100-episode average across all time     - Average for all episodes across time     """     print(report % (         np.mean(rewards[-100:]),         max([np.mean(rewards[i:i+100]) for i in range(len(rewards) - 100)]),         np.mean(rewards),         episode))   def main():     env = gym.make('FrozenLake-v0')  # create the game     env.seed(0)  # make results reproducible     rewards = []      Q = np.zeros((env.observation_space.n, env.action_space.n))     for episode in range(1, num_episodes + 1):         state = env.reset()         episode_reward = 0         while True:             noise = np.random.random((1, env.action_space.n)) / (episode**2.)             action = np.argmax(Q[state, :] + noise)             state2, reward, done, _ = env.step(action)             Qtarget = reward + discount_factor * np.max(Q[state2, :])             Q[state, action] = (1-learning_rate) * Q[state, action] + learning_rate * Qtarget             episode_reward += reward             state = state2             if done:                 rewards.append(episode_reward)                 if episode % report_interval == 0:                     print_report(rewards, episode)                 break     print_report(rewards, -1)  if __name__ == '__main__':     main() 

Lưu file , thoát khỏi editor và chạy tập lệnh:

  • python bot_3_q_table.py

Đầu ra của bạn sẽ phù hợp với những điều sau:

Output
100-ep Average: 0.11 . Best 100-ep Average: 0.12 . Average: 0.03 (Episode 500) 100-ep Average: 0.25 . Best 100-ep Average: 0.24 . Average: 0.09 (Episode 1000) 100-ep Average: 0.39 . Best 100-ep Average: 0.48 . Average: 0.19 (Episode 1500) 100-ep Average: 0.43 . Best 100-ep Average: 0.55 . Average: 0.25 (Episode 2000) 100-ep Average: 0.44 . Best 100-ep Average: 0.55 . Average: 0.29 (Episode 2500) 100-ep Average: 0.64 . Best 100-ep Average: 0.68 . Average: 0.32 (Episode 3000) 100-ep Average: 0.63 . Best 100-ep Average: 0.71 . Average: 0.36 (Episode 3500) 100-ep Average: 0.56 . Best 100-ep Average: 0.78 . Average: 0.40 (Episode 4000) 100-ep Average: 0.56 . Best 100-ep Average: 0.78 . Average: 0.40 (Episode -1)

Đến đây bạn đã có bot đầu tiên của bạn cho các trò chơi, nhưng hãy đặt phần thưởng trung bình 0.78 vào góc nhìn. Theo trang Gym FrozenLake , "giải quyết" trò chơi nghĩa là đạt được mức trung bình 100 tập là 0.78 . Một cách không chính thức, "giải quyết" nghĩa là "chơi trò chơi rất tốt". Trong thời gian không kỷ lục, đặc vụ Q-table có thể giải FrozenLake trong 4000 tập.

Tuy nhiên, trò chơi có thể phức tạp hơn. Ở đây, bạn đã sử dụng một bảng để lưu trữ tất cả 144 trạng thái có thể, nhưng hãy xem xét tic tac toe trong đó có 19.683 trạng thái có thể. Tương tự như vậy, hãy xem xét Space Invaders nơi có quá nhiều trạng thái có thể được đếm. Bàn Q không bền vững khi các trò chơi ngày càng phức tạp. Vì lý do này, bạn cần một số cách để tính gần đúng bảng Q. Khi bạn tiếp tục thử nghiệm ở bước tiếp theo, bạn sẽ thiết kế một hàm có thể chấp nhận các trạng thái và hành động làm đầu vào và xuất ra giá trị Q.

Bước 4 - Xây dựng tác nhân Q-learning sâu cho Frozen Lake

Trong học tập củng cố, mạng nơ-ron dự đoán hiệu quả giá trị của Q dựa trên đầu vào stateaction , sử dụng một bảng để lưu trữ tất cả các giá trị có thể, nhưng điều này trở nên không ổn định trong các trò chơi phức tạp. Thay vào đó, học tăng cường sâu sử dụng mạng nơ-ron để tính gần đúng hàm Q. Để biết thêm chi tiết, hãy xem Tìm hiểu sâu về Q-Learning .

Để làm quen với Tensorflow , một thư viện học sâu mà bạn đã cài đặt ở Bước 1, bạn sẽ thực hiện lại tất cả logic được sử dụng cho đến nay với các bản tóm tắt của Tensorflow và bạn sẽ sử dụng mạng nơ-ron để tính gần đúng hàm Q của bạn . Tuy nhiên, mạng nơron của bạn sẽ cực kỳ đơn giản: Q(s) kết quả Q(s) là ma trận W nhân với s đầu vào của bạn. Đây được gọi là mạng nơ-ron với một lớp được kết nối đầy đủ :

Q(s) = Ws 

Để nhắc lại, mục tiêu là thực hiện lại tất cả logic từ các bot mà ta đã xây dựng bằng cách sử dụng các trừu tượng của Tensorflow. Điều này sẽ làm cho các hoạt động của bạn hiệu quả hơn, vì Tensorflow sau đó có thể thực hiện tất cả các phép tính trên GPU.

Bắt đầu bằng cách sao chép tập lệnh bảng Q của bạn từ Bước 3:

  • cp bot_3_q_table.py bot_4_q_network.py

Sau đó, mở file mới bằng nano hoặc editor bạn muốn :

  • nano bot_4_q_network.py

Đầu tiên, hãy cập nhật comment ở đầu file :

/AtariBot/bot_4_q_network.py
""" Bot 4 -- Use Q-learning network to train bot """  . . . 

Tiếp theo, nhập gói Tensorflow bằng cách thêm chỉ thị import ngay bên dưới import random . Ngoài ra, hãy thêm tf.set_radon_seed(0) ngay bên dưới np.random.seed(0) . Điều này sẽ đảm bảo kết quả của tập lệnh này sẽ có thể lặp lại trên tất cả các phiên:

/AtariBot/bot_4_q_network.py
. . . import random import tensorflow as tf random.seed(0) np.random.seed(0) tf.set_random_seed(0) . . . 

Xác định lại các siêu tham số của bạn ở đầu file để trùng với phần sau và thêm một hàm có tên là exploration_probability , hàm này sẽ trả về xác suất thăm dò ở mỗi bước. Lưu ý , trong ngữ cảnh này, “thăm dò” nghĩa là thực hiện một hành động ngẫu nhiên, thay vì thực hiện hành động được các ước tính giá trị Q đề xuất:

/AtariBot/bot_4_q_network.py
. . . num_episodes = 4000 discount_factor = 0.99 learning_rate = 0.15 report_interval = 500 exploration_probability = lambda episode: 50. / (episode + 10) report = '100-ep Average: %.2f . Best 100-ep Average: %.2f . Average: %.2f ' \          '(Episode %d)' . . . 

Tiếp theo, bạn sẽ thêm chức năng mã hóa một nóng . Nói tóm lại, mã hóa một nóng là một quá trình mà qua đó các biến được chuyển đổi thành một dạng giúp các thuật toán học máy đưa ra dự đoán tốt hơn. Nếu bạn muốn tìm hiểu thêm về mã hóa một lần, bạn có thể xem Ví dụ về đối thủ trong Thị giác máy tính: Cách xây dựng sau đó đánh lừa bộ lọc chó dựa trên cảm xúc .

Ngay bên dưới report = ... , thêm một hàm one_hot :

/AtariBot/bot_4_q_network.py
. . . report = '100-ep Average: %.2f . Best 100-ep Average: %.2f . Average: %.2f ' \          '(Episode %d)'  def one_hot(i: int, n: int) -> np.array:     """Implements one-hot encoding by selecting the ith standard basis vector"""     return np.identity(n)[i].reshape((1, -1))  def print_report(rewards: List, episode: int): . . . 

Tiếp theo, bạn sẽ viết lại logic thuật toán của bạn bằng cách sử dụng các trừu tượng của Tensorflow. Tuy nhiên, trước khi làm điều đó, trước tiên bạn cần tạo trình giữ chỗ cho dữ liệu của bạn .

Trong chức năng main của bạn, ngay bên dưới rewards=[] , hãy chèn nội dung được đánh dấu sau. Tại đây, bạn xác định trình giữ chỗ cho quan sát của bạn tại thời điểm t (dưới dạng obs_t_ph ) và thời điểm t + 1 (dưới dạng obs_tp1_ph ), cũng như trình giữ chỗ cho hành động, phần thưởng và mục tiêu Q của bạn:

/AtariBot/bot_4_q_network.py
. . . def main():     env = gym.make('FrozenLake-v0')  # create the game     env.seed(0)  # make results reproducible     rewards = []      # 1. Setup placeholders     n_obs, n_actions = env.observation_space.n, env.action_space.n     obs_t_ph = tf.placeholder(shape=[1, n_obs], dtype=tf.float32)     obs_tp1_ph = tf.placeholder(shape=[1, n_obs], dtype=tf.float32)     act_ph = tf.placeholder(tf.int32, shape=())     rew_ph = tf.placeholder(shape=(), dtype=tf.float32)     q_target_ph = tf.placeholder(shape=[1, n_actions], dtype=tf.float32)      Q = np.zeros((env.observation_space.n, env.action_space.n))     for episode in range(1, num_episodes + 1):         . . . 

Ngay bên dưới dòng bắt đầu bằng q_target_ph = , hãy chèn các dòng được đánh dấu sau. Mã này bắt đầu tính toán của bạn bằng cách tính Q (s, a) cho tất cả a để tạo q_currentQ (s ', a') cho tất cả a ' để tạo q_target :

/AtariBot/bot_4_q_network.py
    . . .     rew_ph = tf.placeholder(shape=(), dtype=tf.float32)     q_target_ph = tf.placeholder(shape=[1, n_actions], dtype=tf.float32)      # 2. Setup computation graph     W = tf.Variable(tf.random_uniform([n_obs, n_actions], 0, 0.01))     q_current = tf.matmul(obs_t_ph, W)     q_target = tf.matmul(obs_tp1_ph, W)      Q = np.zeros((env.observation_space.n, env.action_space.n))     for episode in range(1, num_episodes + 1):         . . . 

ngay bên dưới dòng cuối cùng bạn đã thêm, hãy chèn mã được tô sáng sau. Hai dòng đầu tiên tương đương với dòng được thêm vào trong Bước 3 tính Qtarget , trong đó Qtarget = reward + discount_factor * np.max(Q[state2, :]) . Hai dòng tiếp theo cài đặt khoản lỗ của bạn, trong khi dòng cuối cùng tính toán hành động tối đa hóa giá trị Q của bạn:

/AtariBot/bot_4_q_network.py
    . . .     q_current = tf.matmul(obs_t_ph, W)     q_target = tf.matmul(obs_tp1_ph, W)      q_target_max = tf.reduce_max(q_target_ph, axis=1)     q_target_sa = rew_ph + discount_factor * q_target_max     q_current_sa = q_current[0, act_ph]     error = tf.reduce_sum(tf.square(q_target_sa - q_current_sa))     pred_act_ph = tf.argmax(q_current, 1)      Q = np.zeros((env.observation_space.n, env.action_space.n))     for episode in range(1, num_episodes + 1):         . . . 

Sau khi cài đặt thuật toán và hàm mất mát, hãy xác định trình tối ưu hóa của bạn:

/AtariBot/bot_4_q_network.py
    . . .     error = tf.reduce_sum(tf.square(q_target_sa - q_current_sa))     pred_act_ph = tf.argmax(q_current, 1)      # 3. Setup optimization     trainer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)     update_model = trainer.minimize(error)      Q = np.zeros((env.observation_space.n, env.action_space.n))     for episode in range(1, num_episodes + 1):         . . . 

Tiếp theo, cài đặt phần thân của vòng lặp trò chơi. Để thực hiện việc này, hãy chuyển dữ liệu tới trình giữ chỗ Tensorflow và các phần tóm tắt của Tensorflow sẽ xử lý tính toán trên GPU, trả về kết quả của thuật toán.

Bắt đầu bằng cách xóa Q-table và logic cũ. Cụ thể, xóa các đường để xác định Q (ngay trước khi for vòng lặp), noise (trong while vòng lặp), action , Qtarget , và Q[state, action] . Đổi tên state thành obs_tstate2 thành obs_tp1 để căn chỉnh với trình giữ chỗ Tensorflow mà bạn đã đặt trước đó. Khi hoàn tất, vòng lặp for của bạn sẽ khớp như sau:

/AtariBot/bot_4_q_network.py
    . . .     # 3. Setup optimization     trainer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)     update_model = trainer.minimize(error)      for episode in range(1, num_episodes + 1):         obs_t = env.reset()         episode_reward = 0         while True:              obs_tp1, reward, done, _ = env.step(action)              episode_reward += reward             obs_t = obs_tp1             if done:                 ... 

Ngay phía trên vòng lặp for , thêm hai dòng được đánh dấu sau. Các dòng này khởi tạo một phiên Tensorflow, từ đó quản lý các tài nguyên cần thiết để chạy các hoạt động trên GPU. Dòng thứ hai khởi tạo tất cả các biến trong biểu đồ tính toán của bạn; ví dụ: khởi tạo trọng số thành 0 trước khi cập nhật chúng. Ngoài ra, bạn sẽ lồng vòng lặp for trong câu lệnh with , vì vậy hãy thụt lề toàn bộ vòng lặp for bốn dấu cách:

/AtariBot/bot_4_q_network.py
    . . .     trainer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)         update_model = trainer.minimize(error)      with tf.Session() as session:         session.run(tf.global_variables_initializer())          for episode in range(1, num_episodes + 1):             obs_t = env.reset()             ... 

Trước dòng đọc obs_tp1, reward, done, _ = env.step(action) , hãy chèn các dòng sau để tính toán action . Mã này đánh giá trình giữ chỗ tương ứng và thay thế hành động bằng một hành động ngẫu nhiên với một số xác suất:

/AtariBot/bot_4_q_network.py
            . . .             while True:                 # 4. Take step using best action or random action                 obs_t_oh = one_hot(obs_t, n_obs)                 action = session.run(pred_act_ph, feed_dict={obs_t_ph: obs_t_oh})[0]                 if np.random.rand(1) < exploration_probability(episode):                     action = env.action_space.sample()                 . . . 

Sau dòng chứa env.step(action) , hãy chèn dòng sau để huấn luyện mạng nơ-ron ước tính hàm Q-value của bạn:

/AtariBot/bot_4_q_network.py
                . . .                 obs_tp1, reward, done, _ = env.step(action)                  # 5. Train model                 obs_tp1_oh = one_hot(obs_tp1, n_obs)                 q_target_val = session.run(q_target, feed_dict={obs_tp1_ph: obs_tp1_oh})                 session.run(update_model, feed_dict={                     obs_t_ph: obs_t_oh,                     rew_ph: reward,                     q_target_ph: q_target_val,                     act_ph: action                 })                 episode_reward += reward                 . . . 

Tệp cuối cùng của bạn sẽ trùng với tệp này được lưu trữ trên GitHub . Lưu file , thoát khỏi editor và chạy tập lệnh:

  • python bot_4_q_network.py

Đầu ra của bạn sẽ kết thúc chính xác bằng những thứ sau:

Output
100-ep Average: 0.11 . Best 100-ep Average: 0.11 . Average: 0.05 (Episode 500) 100-ep Average: 0.41 . Best 100-ep Average: 0.54 . Average: 0.19 (Episode 1000) 100-ep Average: 0.56 . Best 100-ep Average: 0.73 . Average: 0.31 (Episode 1500) 100-ep Average: 0.57 . Best 100-ep Average: 0.73 . Average: 0.36 (Episode 2000) 100-ep Average: 0.65 . Best 100-ep Average: 0.73 . Average: 0.41 (Episode 2500) 100-ep Average: 0.65 . Best 100-ep Average: 0.73 . Average: 0.43 (Episode 3000) 100-ep Average: 0.69 . Best 100-ep Average: 0.73 . Average: 0.46 (Episode 3500) 100-ep Average: 0.77 . Best 100-ep Average: 0.79 . Average: 0.48 (Episode 4000) 100-ep Average: 0.77 . Best 100-ep Average: 0.79 . Average: 0.48 (Episode -1)

Đến đây bạn đã đào tạo nhân viên Q-learning sâu đầu tiên của bạn . Đối với một trò chơi đơn giản như FrozenLake, nhân viên Q-learning sâu của bạn cần 4000 tập để đào tạo. Hãy tưởng tượng nếu trò chơi phức tạp hơn nhiều. Có bao nhiêu mẫu đào tạo sẽ yêu cầu để đào tạo? Hóa ra, đại lý có thể yêu cầu hàng triệu mẫu. Số lượng mẫu cần thiết được gọi là độ phức tạp của mẫu , một khái niệm được khám phá thêm trong phần tiếp theo.

Hiểu sự cân bằng phương sai lệch

Nói chung, độ phức tạp của mẫu trái ngược với độ phức tạp của mô hình trong học máy:

  1. Độ phức tạp của mô hình : Người ta muốn một mô hình đủ phức tạp để giải quyết vấn đề của họ. Ví dụ, một mô hình đơn giản như một đoạn thẳng không đủ phức tạp để dự đoán quỹ đạo của ô tô.
  2. Độ phức tạp của mẫu : Người ta muốn một mô hình không yêu cầu nhiều mẫu. Điều này có thể là do họ có quyền truy cập hạn chế vào dữ liệu được gắn nhãn, không đủ sức mạnh tính toán, bộ nhớ hạn chế, v.v.

Giả sử ta có hai mô hình, một đơn giản và một cực kỳ phức tạp. Để cả hai mô hình đạt được hiệu suất như nhau, phương sai lệch cho ta biết rằng mô hình cực kỳ phức tạp cần nhiều mẫu hơn theo cấp số nhân để đào tạo. Trường hợp điển hình: tác nhân Q-learning dựa trên mạng nơ-ron của bạn cần 4000 tập để giải FrozenLake. Thêm một lớp thứ hai vào tác nhân mạng thần kinh tăng gấp bốn lần số tập huấn luyện cần thiết. Với các mạng nơ-ron ngày càng phức tạp, sự phân chia này chỉ tăng lên. Để duy trì cùng một tỷ lệ lỗi, việc tăng độ phức tạp của mô hình sẽ làm tăng độ phức tạp của mẫu theo cấp số nhân. Tương tự như vậy, giảm độ phức tạp của mẫu làm giảm độ phức tạp của mô hình. Do đó, ta không thể tối đa hóa độ phức tạp của mô hình và giảm thiểu độ phức tạp của mẫu theo mong muốn của trái tim mình.

Tuy nhiên, ta có thể tận dụng kiến thức của bạn về sự đánh đổi này. Để có cách giải thích trực quan về toán học đằng sau sự phân rã phương sai lệch , hãy xem Tìm hiểu về sự cân bằng phương sai lệch . Ở cấp độ cao, phân tích phương sai thiên lệch là sự phân tích “sai số thực” thành hai thành phần: thành phần thiên vị và phương sai. Ta gọi "lỗi đúng" là lỗi bình phương trung bình (MSE), là sự khác biệt dự kiến giữa các nhãn được dự đoán của ta và các nhãn thực. Sau đây là biểu đồ cho thấy sự thay đổi của "true error" khi độ phức tạp của mô hình tăng lên:

Đường cong lỗi trung bình bình phương

Bước 5 - Xây dựng một đặc vụ ít nhất cho Frozen Lake

Phương pháp bình phương nhỏ nhất , còn gọi là hồi quy tuyến tính , là một phương tiện phân tích hồi quy được sử dụng rộng rãi trong lĩnh vực toán học và khoa học dữ liệu. Trong học máy, nó thường được sử dụng để tìm mô hình tuyến tính tối ưu của hai tham số hoặc tập dữ liệu.

Trong Bước 4, bạn đã xây dựng một mạng nơ-ron để tính giá trị Q. Thay vì mạng nơron, trong bước này, bạn sẽ sử dụng hồi quy sườn núi , một biến thể của bình phương nhỏ nhất, để tính toán các giá trị Q-vectơ này. Hy vọng rằng với một mô hình không phức tạp như hình vuông nhỏ nhất, việc giải quyết trò chơi sẽ yêu cầu ít tập luyện hơn.

Bắt đầu bằng cách sao chép tập lệnh từ Bước 3:

  • cp bot_3_q_table.py bot_5_ls.py

Mở file mới:

  • nano bot_5_ls.py

, hãy cập nhật comment ở đầu file mô tả những gì tập lệnh này sẽ thực hiện:

/AtariBot/bot_4_q_network.py
""" Bot 5 -- Build least squares q-learning agent for FrozenLake """  . . . 

Trước khối nhập gần đầu file của bạn, hãy thêm hai nhập khác để kiểm tra loại:

/AtariBot/bot_5_ls.py
. . . from typing import Tuple from typing import Callable from typing import List import gym . . . 

Trong danh sách các siêu tham số của bạn, hãy thêm một siêu tham số khác, w_lr , để kiểm soát tốc độ học của hàm Q thứ hai. Ngoài ra, cập nhật số lượng tập lên 5000 và hệ số chiết khấu thành 0.85 . Bằng cách thay đổi cả siêu tham số num_episodesdiscount_factor thành các giá trị lớn hơn, tác nhân sẽ có thể đưa ra hiệu suất mạnh hơn:

/AtariBot/bot_5_ls.py
. . . num_episodes = 5000 discount_factor = 0.85 learning_rate = 0.9 w_lr = 0.5 report_interval = 500 . . . 

Trước hàm print_report của bạn, hãy thêm hàm bậc cao sau. Nó trả về lambda - một hàm ẩn danh - trừu tượng hóa mô hình:

/AtariBot/bot_5_ls.py
. . . report_interval = 500 report = '100-ep Average: %.2f . Best 100-ep Average: %.2f . Average: %.2f ' \          '(Episode %d)'  def makeQ(model: np.array) -> Callable[[np.array], np.array]:     """Returns a Q-function, which takes state -> distribution over actions"""     return lambda X: X.dot(model)  def print_report(rewards: List, episode: int):     . . . 

Sau makeQ , hãy thêm một hàm khác, initialize , khởi tạo mô hình bằng các giá trị được phân phối chuẩn:

/AtariBot/bot_5_ls.py
. . . def makeQ(model: np.array) -> Callable[[np.array], np.array]:     """Returns a Q-function, which takes state -> distribution over actions"""     return lambda X: X.dot(model)  def initialize(shape: Tuple):     """Initialize model"""     W = np.random.normal(0.0, 0.1, shape)     Q = makeQ(W)     return W, Q  def print_report(rewards: List, episode: int):     . . . 

Sau khối initialize , hãy thêm một phương thức train để tính toán giải pháp dạng đóng hồi quy sườn núi, sau đó cân mô hình cũ với mô hình mới. Nó trả về cả mô hình và hàm Q được trừu tượng hóa:

/AtariBot/bot_5_ls.py
. . . def initialize(shape: Tuple):     ...     return W, Q  def train(X: np.array, y: np.array, W: np.array) -> Tuple[np.array, Callable]:     """Train the model, using solution to ridge regression"""     I = np.eye(X.shape[1])     newW = np.linalg.inv(X.T.dot(X) + 10e-4 * I).dot(X.T.dot(y))     W = w_lr * newW + (1 - w_lr) * W     Q = makeQ(W)     return W, Q  def print_report(rewards: List, episode: int):     . . . 

Sau khi train , hãy thêm một hàm cuối cùng, one_hot , để thực hiện mã hóa một lần cho các trạng thái và hành động của bạn:

/AtariBot/bot_5_ls.py
. . . def train(X: np.array, y: np.array, W: np.array) -> Tuple[np.array, Callable]:     ...     return W, Q  def one_hot(i: int, n: int) -> np.array:     """Implements one-hot encoding by selecting the ith standard basis vector"""     return np.identity(n)[i]  def print_report(rewards: List, episode: int):     . . . 

Sau đó, bạn cần phải sửa đổi logic đào tạo. Trong tập lệnh trước bạn đã viết, bảng Q được cập nhật mỗi lần lặp lại. Tuy nhiên, tập lệnh này sẽ thu thập các mẫu và nhãn mỗi bước và đào tạo một mô hình mới sau mỗi 10 bước. Ngoài ra, thay vì giữ bảng Q hoặc mạng nơ ron, nó sẽ sử dụng mô hình bình phương nhỏ nhất để dự đoán giá trị Q.

Đi tới hàm main và thay thế định nghĩa của bảng Q = np.zeros(...) ( Q = np.zeros(...) ) bằng định nghĩa sau:

/AtariBot/bot_5_ls.py
. . . def main():     ...     rewards = []      n_obs, n_actions = env.observation_space.n, env.action_space.n     W, Q = initialize((n_obs, n_actions))     states, labels = [], []     for episode in range(1, num_episodes + 1):         . . . 

Cuộn xuống trước vòng lặp for . Ngay bên dưới phần này, thêm các dòng sau để đặt lại các states và danh sách labels nếu có quá nhiều thông tin được lưu trữ:

/AtariBot/bot_5_ls.py
. . . def main():     ...     for episode in range(1, num_episodes + 1):         if len(states) >= 10000:             states, labels = [], []             . . . 

Sửa đổi dòng ngay sau dòng này, định nghĩa state = env.reset() , để nó trở thành dòng sau. Điều này sẽ mã hóa trạng thái một nóng ngay lập tức, vì tất cả các hoạt động sử dụng của nó sẽ yêu cầu vectơ một nóng:

/AtariBot/bot_5_ls.py
. . .     for episode in range(1, num_episodes + 1):         if len(states) >= 10000:             states, labels = [], []         state = one_hot(env.reset(), n_obs) . . . 

Trước dòng đầu tiên trong vòng lặp while trò chơi chính của bạn, hãy sửa đổi danh sách các states :

/AtariBot/bot_5_ls.py
. . .     for episode in range(1, num_episodes + 1):         ...         episode_reward = 0         while True:             states.append(state)             noise = np.random.random((1, env.action_space.n)) / (episode**2.)             . . . 

Cập nhật tính toán cho action , giảm xác suất nhiễu và sửa đổi đánh giá hàm Q:

/AtariBot/bot_5_ls.py
. . .         while True:             states.append(state)             noise = np.random.random((1, n_actions)) / episode             action = np.argmax(Q(state) + noise)             state2, reward, done, _ = env.step(action)             . . . 

Thêm một version hot nhất của state2 và sửa đổi lệnh gọi hàm Q trong định nghĩa của bạn cho Qtarget như sau:

/AtariBot/bot_5_ls.py
. . .         while True:             ...             state2, reward, done, _ = env.step(action)              state2 = one_hot(state2, n_obs)             Qtarget = reward + discount_factor * np.max(Q(state2))             . . . 

Xóa dòng cập nhật Q[state,action] = ... và thay thế bằng các dòng sau. Mã này nhận kết quả của mô hình hiện tại và chỉ cập nhật giá trị trong kết quả này tương ứng với hành động hiện tại được thực hiện. Do đó, giá trị Q cho các hành động khác không bị mất:

/AtariBot/bot_5_ls.py
. . .             state2 = one_hot(state2, n_obs)             Qtarget = reward + discount_factor * np.max(Q(state2))             label = Q(state)             label[action] = (1 - learning_rate) * label[action] + learning_rate * Qtarget             labels.append(label)              episode_reward += reward             . . . 

Ngay sau state = state2 , hãy thêm cập nhật định kỳ vào mô hình. Điều này đào tạo mô hình của bạn sau mỗi 10 bước thời gian:

/AtariBot/bot_5_ls.py
. . .             state = state2             if len(states) % 10 == 0:                 W, Q = train(np.array(states), np.array(labels), W)             if done:             . . . 

Kiểm tra kỹ xem file của bạn có trùng với mã nguồn không . Sau đó, lưu file , thoát khỏi editor và chạy tập lệnh:

  • python bot_5_ls.py

Điều này sẽ xuất ra như sau:

Output
100-ep Average: 0.17 . Best 100-ep Average: 0.17 . Average: 0.09 (Episode 500) 100-ep Average: 0.11 . Best 100-ep Average: 0.24 . Average: 0.10 (Episode 1000) 100-ep Average: 0.08 . Best 100-ep Average: 0.24 . Average: 0.10 (Episode 1500) 100-ep Average: 0.24 . Best 100-ep Average: 0.25 . Average: 0.11 (Episode 2000) 100-ep Average: 0.32 . Best 100-ep Average: 0.31 . Average: 0.14 (Episode 2500) 100-ep Average: 0.35 . Best 100-ep Average: 0.38 . Average: 0.16 (Episode 3000) 100-ep Average: 0.59 . Best 100-ep Average: 0.62 . Average: 0.22 (Episode 3500) 100-ep Average: 0.66 . Best 100-ep Average: 0.66 . Average: 0.26 (Episode 4000) 100-ep Average: 0.60 . Best 100-ep Average: 0.72 . Average: 0.30 (Episode 4500) 100-ep Average: 0.75 . Best 100-ep Average: 0.82 . Average: 0.34 (Episode 5000) 100-ep Average: 0.75 . Best 100-ep Average: 0.82 . Average: 0.34 (Episode -1)

Hãy nhớ lại rằng, theo trang Gym FrozenLake , "giải quyết" trò chơi nghĩa là đạt được mức trung bình 100 tập là 0,78. Ở đây, đặc vụ đạt trung bình 0,82, nghĩa là nó có thể giải quyết trò chơi trong 5000 tập. Mặc dù điều này không giải quyết được trò chơi trong ít tập hơn, nhưng phương pháp bình phương nhỏ nhất cơ bản này vẫn có thể giải quyết một trò chơi đơn giản với số tập luyện gần giống nhau. Mặc dù mạng nơ-ron của bạn có thể phức tạp hơn, nhưng bạn đã chứng minh rằng các mô hình đơn giản là đủ cho FrozenLake.

Cùng với đó, bạn đã khám phá ba tác nhân Q-learning: một sử dụng bảng Q, một sử dụng mạng nơ-ron và một tác nhân thứ ba sử dụng bình phương nhỏ nhất. Tiếp theo, bạn sẽ xây dựng một tác nhân học tập củng cố sâu cho một trò chơi phức tạp hơn: Space Invaders.

Bước 6 - Tạo tác nhân Q-learning sâu cho những kẻ xâm lược không gian

Giả sử bạn đã điều chỉnh độ phức tạp mô hình và độ phức tạp mẫu của thuật toán Q-learning trước đó một cách hoàn hảo, dù bạn chọn phương pháp mạng nơron hay phương pháp bình phương nhỏ nhất. Hóa ra, đặc vụ Q-learning không thông minh này vẫn hoạt động kém trên các trò chơi phức tạp hơn, ngay cả với số lượng tập luyện đặc biệt cao. Phần này sẽ bao gồm hai kỹ thuật có thể cải thiện hiệu suất, sau đó bạn sẽ kiểm tra một nhân viên đã được đào tạo bằng các kỹ thuật này.

Các nhà nghiên cứu tại DeepMind đã phát triển tác nhân có mục đích chung đầu tiên có thể liên tục điều chỉnh hành vi của bạn mà không cần bất kỳ sự can thiệp nào của con người, được phát triển bởi các nhà nghiên cứu tại DeepMind, họ cũng đã đào tạo đại lý của họ để chơi nhiều trò chơi Atari. Bài báo sâu về Q-learning (DQN) ban đầu của DeepMind đã nhận ra hai vấn đề quan trọng:

  1. Trạng thái tương quan : Lấy trạng thái trò chơi của ta tại thời điểm 0, mà ta sẽ gọi là s0 . Giả sử ta cập nhật Q (s0) , theo các luật mà ta đã suy ra trước đó. Bây giờ, lấy trạng thái tại thời điểm 1, mà ta gọi là s1 và cập nhật Q (s1) theo các luật tương tự. Lưu ý trạng thái của trò chơi tại thời điểm 0 rất giống với trạng thái của nó tại thời điểm 1. Ví dụ: trong Space Invaders, những người ngoài hành tinh có thể đã di chuyển mỗi pixel một pixel. Nói ngắn gọn hơn, s0s1 rất giống nhau. Tương tự như vậy, ta cũng mong đợi Q (s0)Q (s1) rất giống nhau, vì vậy việc cập nhật một cái sẽ ảnh hưởng đến cái kia. Điều này dẫn đến các giá trị Q dao động, vì bản cập nhật cho Q (s0) trên thực tế có thể ngược lại với bản cập nhật cho Q (s1) . Chính thức hơn, s0s1tương quan với nhau . Vì hàm Q là xác định nên Q (s1) tương quan với Q (s0) .
  2. Tính không ổn định của hàm Q : Hãy nhớ lại rằng hàm Q vừa là mô hình mà ta đào tạo vừa là nguồn của các nhãn của ta . Giả sử rằng nhãn của ta là các giá trị được chọn ngẫu nhiên thực sự đại diện cho một phân phối , L. Mỗi khi ta cập nhật Q , ta thay đổi L , nghĩa là mô hình của ta đang cố gắng tìm hiểu một mục tiêu di động. Đây là một vấn đề, vì các mô hình ta sử dụng giả định một phân phối cố định.

Để chống lại các trạng thái tương quan và chức năng Q không ổn định:

  1. Người ta có thể giữ một danh sách các trạng thái được gọi là cache phát lại . Mỗi bước thời gian, bạn thêm trạng thái trò chơi mà bạn quan sát được vào vùng đệm phát lại này. Bạn cũng lấy mẫu ngẫu nhiên một tập hợp con các trạng thái từ danh sách này và đào tạo về các trạng thái đó.
  2. Group tại DeepMind đã sao chép Q (s, a) . Một được gọi là Q_current (s, a) , là hàm Q mà bạn cập nhật. Bạn cần một hàm Q khác cho các trạng thái kế thừa, Q_target (s ', a') , mà bạn sẽ không cập nhật. Nhớ lại Q_target (s ', a') được sử dụng để tạo nhãn của bạn. Bằng cách tách Q_current khỏi Q_target và sửa lỗi sau, bạn sửa lỗi phân phối mà nhãn của bạn được lấy mẫu. Sau đó, mô hình học sâu của bạn có thể dành một khoảng thời gian ngắn để học cách phân phối này. Sau một khoảng thời gian, bạn sao chép lại Q_current cho Q_target mới.

Bạn sẽ không tự thực hiện những điều này, nhưng bạn sẽ tải các mô hình đào tạo trước đã được đào tạo với các giải pháp này. Để thực hiện việc này, hãy tạo một folder mới nơi bạn sẽ lưu trữ các thông số của các mô hình này:

  • mkdir models

Sau đó, sử dụng wget để download các thông số của mô hình Space Invaders được đào tạo trước:

  • wget http://models.tensorpack.com/OpenAIGym/SpaceInvaders-v0.tfmodel -P models

Tiếp theo, download một tập lệnh Python chỉ định mô hình được liên kết với các tham số bạn vừa download . Lưu ý mô hình được đào tạo trước này có hai ràng buộc đối với đầu vào cần ghi nhớ:

  • Các trạng thái phải được rút gọn mẫu hoặc giảm kích thước xuống còn 84 x 84.
  • Đầu vào bao gồm bốn trạng thái, xếp chồng lên nhau.

Ta sẽ giải quyết những hạn chế này chi tiết hơn ở phần sau. Hiện tại, hãy download tập lệnh bằng lệnh :

  • wget https://github.com/alvinwan/bots-for-atari-games/raw/master/src/bot_6_a3c.py

Đến đây bạn sẽ chạy tác nhân Space Invaders được đào tạo trước này để xem nó hoạt động như thế nào. Không giống như một số bot trước đây ta đã sử dụng, bạn sẽ viết tập lệnh này từ đầu.

Tạo một file tập lệnh mới:

  • nano bot_6_dqn.py

Bắt đầu tập lệnh này bằng cách thêm chú thích tiêu đề, nhập các tiện ích cần thiết và bắt đầu vòng lặp trò chơi chính:

/AtariBot/bot_6_dqn.py
""" Bot 6 - Fully featured deep q-learning network. """  import cv2 import gym import numpy as np import random import tensorflow as tf from bot_6_a3c import a3c_model   def main():  if __name__ == '__main__':     main() 

Ngay sau khi nhập, đặt các hạt giống ngẫu nhiên để làm cho kết quả của bạn có thể tái tạo. Ngoài ra, hãy xác định một num_episodes siêu num_episodes số sẽ cho script biết có bao nhiêu tập để chạy tác nhân:

/AtariBot/bot_6_dqn.py
. . . import tensorflow as tf from bot_6_a3c import a3c_model random.seed(0)  # make results reproducible tf.set_random_seed(0)  num_episodes = 10  def main():   . . . 

Hai dòng sau khi tuyên bố num_episodes , xác định một downsample hàm downsamples tất cả hình ảnh đến một kích thước 84 x 84. Bạn sẽ downsample tất cả hình ảnh trước khi chuyển chúng vào mạng nơron pretrained, như mô hình pretrained được thí nghiệm trên 84 x 84 hình ảnh:

/AtariBot/bot_6_dqn.py
. . . num_episodes = 10  def downsample(state):     return cv2.resize(state, (84, 84), interpolation=cv2.INTER_LINEAR)[None]  def main():   . . . 

Tạo môi trường trò chơi khi bắt đầu chức năng main của bạn và tạo môi trường để có thể tái tạo kết quả:

/AtariBot/bot_6_dqn.py
. . . def main():     env = gym.make('SpaceInvaders-v0')  # create the game     env.seed(0)  # make results reproducible     . . . 

Trực tiếp sau hạt giống môi trường, khởi tạo một danh sách trống để giữ phần thưởng:

/AtariBot/bot_6_dqn.py
. . . def main():     env = gym.make('SpaceInvaders-v0')  # create the game     env.seed(0)  # make results reproducible     rewards = []     . . . 

Khởi tạo mô hình được đào tạo trước với các thông số mô hình được đào tạo trước mà bạn đã download ở đầu bước này:

/AtariBot/bot_6_dqn.py
. . . def main():     env = gym.make('SpaceInvaders-v0')  # create the game     env.seed(0)  # make results reproducible     rewards = []     model = a3c_model(load='models/SpaceInvaders-v0.tfmodel')     . . . 

Tiếp theo, thêm một số dòng yêu cầu tập lệnh lặp lại trong num_episodes lần num_episodes để tính hiệu suất trung bình và khởi tạo phần thưởng của mỗi tập thành 0. Ngoài ra, thêm một dòng để đặt lại môi trường ( env.reset() ), thu thập trạng thái ban đầu mới trong quy trình , downsample trạng thái ban đầu này với downsample() , và bắt đầu vòng lặp trò chơi sử dụng một while vòng lặp:

/AtariBot/bot_6_dqn.py
. . . def main():     env = gym.make('SpaceInvaders-v0')  # create the game     env.seed(0)  # make results reproducible     rewards = []     model = a3c_model(load='models/SpaceInvaders-v0.tfmodel')     for _ in range(num_episodes):         episode_reward = 0         states = [downsample(env.reset())]         while True:         . . . 

Thay vì chấp nhận một trạng thái tại một thời điểm, mạng nơ-ron mới chấp nhận bốn trạng thái cùng một lúc. Do đó, bạn phải đợi cho đến khi danh sách các states chứa ít nhất bốn trạng thái trước khi áp dụng mô hình đào tạo trước. Thêm các dòng sau vào bên dưới dòng đọc while True: Những điều này yêu cầu tác nhân thực hiện một hành động ngẫu nhiên nếu có ít hơn bốn trạng thái hoặc nối các trạng thái và chuyển nó đến mô hình được đào tạo trước nếu có ít nhất bốn:

/AtariBot/bot_6_dqn.py
        . . .         while True:             if len(states) < 4:                 action = env.action_space.sample()             else:                 frames = np.concatenate(states[-4:], axis=3)                 action = np.argmax(model([frames]))                 . . . 

Sau đó, thực hiện một hành động và cập nhật dữ liệu có liên quan. Thêm version rút gọn của trạng thái được quan sát và cập nhật phần thưởng cho tập này:

/AtariBot/bot_6_dqn.py
        . . .         while True:             ...                 action = np.argmax(model([frames]))             state, reward, done, _ = env.step(action)             states.append(downsample(state))             episode_reward += reward             . . . 

Tiếp theo, thêm các dòng sau để kiểm tra xem tập đã done và nếu có, hãy in tổng phần thưởng của tập và sửa đổi danh sách tất cả các kết quả và phá vỡ vòng lặp while sớm:

/AtariBot/bot_6_dqn.py
        . . .         while True:             ...             episode_reward += reward             if done:                 print('Reward: %d' % episode_reward)                 rewards.append(episode_reward)                 break                 . . . 

Bên ngoài vòng lặp whilefor , hãy in phần thưởng trung bình. Đặt cái này ở cuối chức năng main của bạn:

/AtariBot/bot_6_dqn.py
def main():     ...                 break     print('Average reward: %.2f' % (sum(rewards) / len(rewards))) 

Kiểm tra xem file của bạn có trùng với những thông tin sau:

/AtariBot/bot_6_dqn.py
""" Bot 6 - Fully featured deep q-learning network. """  import cv2 import gym import numpy as np import random import tensorflow as tf from bot_6_a3c import a3c_model random.seed(0)  # make results reproducible tf.set_random_seed(0)  num_episodes = 10   def downsample(state):     return cv2.resize(state, (84, 84), interpolation=cv2.INTER_LINEAR)[None]  def main():     env = gym.make('SpaceInvaders-v0')  # create the game     env.seed(0)  # make results reproducible     rewards = []      model = a3c_model(load='models/SpaceInvaders-v0.tfmodel')     for _ in range(num_episodes):         episode_reward = 0         states = [downsample(env.reset())]         while True:             if len(states) < 4:                 action = env.action_space.sample()             else:                 frames = np.concatenate(states[-4:], axis=3)                 action = np.argmax(model([frames]))             state, reward, done, _ = env.step(action)             states.append(downsample(state))             episode_reward += reward             if done:                 print('Reward: %d' % episode_reward)                 rewards.append(episode_reward)                 break     print('Average reward: %.2f' % (sum(rewards) / len(rewards)))   if __name__ == '__main__':     main() 

Lưu file và thoát khỏi editor . Sau đó, chạy tập lệnh:

  • python bot_6_dqn.py

Đầu ra của bạn sẽ kết thúc bằng những thứ sau:

Output
. . . Reward: 1230 Reward: 4510 Reward: 1860 Reward: 2555 Reward: 515 Reward: 1830 Reward: 4100 Reward: 4350 Reward: 1705 Reward: 4905 Average reward: 2756.00

So sánh điều này với kết quả từ tập lệnh đầu tiên, nơi bạn điều hành một tác nhân ngẫu nhiên cho Space Invaders. Phần thưởng trung bình trong trường hợp đó chỉ khoảng 150, nghĩa là kết quả này tốt hơn hai mươi lần. Tuy nhiên, bạn chỉ chạy mã của bạn trong ba tập, vì nó khá chậm và mức trung bình của ba tập không phải là một số liệu tin cậy . Chạy bộ này hơn 10 tập, trung bình là 2756; hơn 100 tập, mức trung bình là khoảng 2500. Chỉ với những mức trung bình này, bạn có thể thoải mái kết luận rằng đặc vụ của bạn thực sự đang thực hiện một thứ tự cường độ tốt hơn và rằng bạn hiện có một đặc vụ chơi Space Invaders một cách hợp lý.

Tuy nhiên, hãy nhớ lại vấn đề đã được nêu ra trong phần trước liên quan đến độ phức tạp của mẫu. Hóa ra, đặc vụ Space Invaders này cần hàng triệu mẫu để huấn luyện. Trên thực tế, nhân viên này yêu cầu 24 giờ trên bốn GPU Titan X để đào tạo đến cấp độ hiện tại; nói cách khác, cần một lượng máy tính đáng kể để đào tạo nó một cách đầy đủ. Bạn có thể đào tạo một nhân viên có hiệu suất cao tương tự với ít mẫu hơn không? Các bước trước sẽ trang bị cho bạn đủ kiến thức để bắt đầu khám phá câu hỏi này. Có thể sử dụng các mô hình đơn giản hơn nhiều và cân bằng theo phương sai lệch.

Kết luận

Trong hướng dẫn này, bạn đã xây dựng một số bot cho trò chơi và khám phá một khái niệm cơ bản trong học máy được gọi là phương sai lệch (bias-variance). Một câu hỏi tự nhiên tiếp theo là: Bạn có thể xây dựng bot cho các trò chơi phức tạp hơn, chẳng hạn như StarCraft 2 không? Hóa ra, đây là một câu hỏi nghiên cứu đang chờ xử lý, được bổ sung bằng các công cụ open-souce từ các cộng tác viên trên Google, DeepMind và Blizzard. Nếu đây là những vấn đề mà bạn quan tâm, hãy xem các cuộc gọi mở để nghiên cứu tại OpenAI , để biết các vấn đề hiện tại.

Điểm rút ra chính từ hướng dẫn này là sự cân bằng giữa phương sai lệch. Người thực hành học máy tùy thuộc vào việc xem xét ảnh hưởng của độ phức tạp của mô hình. Trong khi có thể tận dụng các mô hình và lớp phức tạp cao trên số lượng máy tính, mẫu và thời gian quá nhiều, việc giảm độ phức tạp của mô hình có thể làm giảm đáng kể tài nguyên cần thiết.


Tags:

Các tin liên quan