# 多智能體(MARL)分類方法 ![](https://i.imgur.com/Urjxt4q.png) a. Analysis of emergent behaviors : 將所有Agent 放入Environment中,不共享及溝通,各自訓練 b. Learning communication : 學習過程中Agent會彼此傳輸訊息及共享來達到更好結果 c. Learning cooperation : 學習過程中Agent透過學習互相合作達到更好結果 d. Agents modeling agents : 若agent間沒有溝通,但是透過看到其他agent的一些外在結果來去model自己的訊息 **Decentralized system (分散式系統)** ![](https://hackmd.io/_uploads/Hy2LQeuVh.png) 分散的方法中,我們獨立單一Agent,而不考慮其他Agents的存在。 在這種情況下,所有Agents都將其他Agents視為環境的一部分。 這是一個非平穩的環境條件,因此不能保證收斂。 **Centralized approach (集合式系統)** ![](https://hackmd.io/_uploads/rkIpmg_En.png) 從所有Agents學習單個策略。 將環境的當前狀態作為Input,Output Agents合作的策略。 獎勵是Global的。 **Self-Play** 在訓練對抗遊戲時非常困難,因為當我們訓練Agent時需要有一個對手協助你,但若對手設定太厲害,agents得到的結果都會是輸,因此訓練出不好的結果 ![](https://i.imgur.com/dMjMGy1.png) 在Self-Play中會幫我們解決此問題-->讓訓練的Agent同時處於玩家和對手,並在Agent升級自己的級別時升級其級別 **輸入** (遊戲狀態)->使用當前遊戲的狀態作為輸入並決定下一步行動 (對手策略)->Agent對於對手的策略也會進行輸入考慮 (策略參數)->Agent的策略參數,會隨著訓練過程不斷修改 **輸出** (行動)->根據遊戲狀態和策略參數,Agent輸出一步行動 (獎勵)->藉由Agent的行動後,環境給予獎勵,Agent再經由 獎勵來改寫自己的策略參數 (新的參數)->不斷更新以便下一次訓練使用 **優點** 可以不需要外部數據就能自動生成訓練數據,並能夠訓練出更優略的行動 --- # Cooperative environments 合作環境 想法1: 放入2個Agent,並設計一個有開關的門(人必須踩在上面),門後有金幣,而這2個Agent必須合作才有辦法拿到金幣並通關。 想法2: 設計一個箱子而這個箱子一定要2個或以上的Agent才可以推動,用來逃脫或是用來壓按鈕。 --- **Competitive/Adversarial environments 競爭/對抗環境** 有2個隊伍都想要打敗對方獲得勝利,像是單人的羽球和排球就是簡單的例子。 底下是製作了單人排球的遊戲 ![](https://i.imgur.com/u90TqTj.png) 規則:球不能落地 目標:讓球掉到對方的場地上 代理:2個不同的多代理 **環境設置:** 先放置球場、邊界、球網和球 再來放置紫方和藍方需要的物件,球員(Agent)和地板 物件放置完,就要開始設置環境的變數 首先是整個遊戲的環境(遊戲有哪些物件,遊戲規則) ``` public enum Team { Blue = 0, Purple = 1, Default = 2 } public enum Event //設置界線區域 { HitPurpleGoal = 0, //藍方地板(紫方得分) HitBlueGoal = 1, //紫方地板(藍方得分) HitOutOfBounds = 2, //界外 HitIntoBlueArea = 3, //藍方區域 HitIntoPurpleArea = 4 //紫方區域 } ``` ``` void Start() { blueAgentRb = blueAgent.GetComponent<Rigidbody>(); purpleAgentRb = purpleAgent.GetComponent<Rigidbody>(); ballRb = ball.GetComponent<Rigidbody>(); var spawnSideList = new List<int> { -1, 1 }; ballSpawnSide = spawnSideList[Random.Range(0, 2)]; //一開始發球位置 blueGoalRenderer = blueGoal.GetComponent<Renderer>(); purpleGoalRenderer = purpleGoal.GetComponent<Renderer>(); RenderersList.Add(blueGoalRenderer); RenderersList.Add(purpleGoalRenderer); volleyballSettings = FindObjectOfType<VolleyballSettings>(); ResetScene(); } ``` ``` public void ResolveEvent(Event triggerEvent) //是否有碰觸trigger(觸發器) { switch (triggerEvent) { case Event.HitOutOfBounds: if (lastHitter == Team.Blue) {// apply penalty to blue agent } else if (lastHitter == Team.Purple) {// apply penalty to purple agent } blueAgent.EndEpisode(); purpleAgent.EndEpisode(); ResetScene(); break; case Event.HitBlueGoal: // blue wins // turn floor blue StartCoroutine(GoalScoredSwapGroundMaterial(volleyballSettings.blueGoalMaterial, RenderersList, .5f)); blueAgent.EndEpisode(); purpleAgent.EndEpisode(); ResetScene(); break; case Event.HitPurpleGoal: // purple wins // turn floor purple StartCoroutine(GoalScoredSwapGroundMaterial(volleyballSettings.purpleGoalMaterial, RenderersList, .5f)); blueAgent.EndEpisode(); purpleAgent.EndEpisode(); ResetScene(); break; case Event.HitIntoBlueArea: if (lastHitter == Team.Purple) { purpleAgent.AddReward(1); } break; case Event.HitIntoPurpleArea: if (lastHitter == Team.Blue) { blueAgent.AddReward(1); } break; } } ``` episode 則代表了智能體與環境之間的互動過程,包括從環境中觀測到狀態,採取動作,接收環境回饋等過程。 ``` public void ResetScene() { resetTimer = 0; lastHitter = Team.Default; // reset last hitter foreach (var agent in AgentsList) { // randomise starting positions and rotations var randomPosX = Random.Range(-2f, 2f); var randomPosZ = Random.Range(-2f, 2f); var randomPosY = Random.Range(0.5f, 3.75f); // depends on jump height var randomRot = Random.Range(-45f, 45f); agent.transform.localPosition = new Vector3(randomPosX, randomPosY, randomPosZ); agent.transform.eulerAngles = new Vector3(0, randomRot, 0); agent.GetComponent<Rigidbody>().velocity = default(Vector3); } ResetBall(); } void ResetBall() { var randomPosX = Random.Range(-2f, 2f); var randomPosZ = Random.Range(6f, 10f); var randomPosY = Random.Range(6f, 8f); ballSpawnSide = -1 * ballSpawnSide; if (ballSpawnSide == -1) { ball.transform.localPosition = new Vector3(randomPosX, randomPosY, randomPosZ); } else if (ballSpawnSide == 1) { ball.transform.localPosition = new Vector3(randomPosX, randomPosY, -1 * randomPosZ); } ballRb.angularVelocity = Vector3.zero; ballRb.velocity = Vector3.zero; } ``` 排球上的設置 ``` void OnTriggerEnter(Collider other) //觸發區域 { if (other.gameObject.CompareTag("boundary")) //界外 { // ball went out of bounds envController.ResolveEvent(Event.HitOutOfBounds); } else if (other.gameObject.CompareTag("blueBoundary")) //藍方界外 (紫得分) { // ball hit into blue side envController.ResolveEvent(Event.HitIntoBlueArea); } else if (other.gameObject.CompareTag("purpleBoundary")) //紫方界外 (藍得分) { // ball hit into purple side envController.ResolveEvent(Event.HitIntoPurpleArea); } else if (other.gameObject.CompareTag("purpleGoal")) //藍方地板 (紫得分) { // ball hit purple goal (blue side court) envController.ResolveEvent(Event.HitPurpleGoal); } else if (other.gameObject.CompareTag("blueGoal")) //紫方界外 (藍得分) { // ball hit blue goal (purple side court) envController.ResolveEvent(Event.HitBlueGoal); } } ``` 環境設置完後會像是這樣 {%youtube oWDZuzzNhCo %} 有了環境再來就是Agent的部分,而強化學習最重要的就是觀察和獎勵 ``` public override void CollectObservations(VectorSensor sensor) {觀察} public override void OnActionReceived(ActionBuffers actions) {行動與獎勵的給予} public override void Heuristic(in ActionBuffers actionsOut) {鍵盤的操作} ``` ``` public override void CollectObservations(VectorSensor sensor) { // Agent rotation (1 float) sensor.AddObservation(this.transform.rotation.y); // Vector from agent to ball (direction to ball) (3 floats) Vector3 toBall = new Vector3((ballRb.transform.position.x - this.transform.position.x) * agentRot, (ballRb.transform.position.y - this.transform.position.y), (ballRb.transform.position.z - this.transform.position.z) * agentRot); sensor.AddObservation(toBall.normalized); // Distance from the ball (1 float) // 與球的距離,magnitude表示這個向量的長度 sensor.AddObservation(toBall.magnitude); // Agent velocity (3 floats) sensor.AddObservation(agentRb.velocity); // Ball velocity (3 floats) sensor.AddObservation(ballRb.velocity.y); sensor.AddObservation(ballRb.velocity.z * agentRot); sensor.AddObservation(ballRb.velocity.x * agentRot); } ``` ``` public override void OnActionReceived(ActionBuffers actionBuffers) { MoveAgent(actionBuffers.DiscreteActions); } public void MoveAgent(ActionSegment<int> act) { var grounded = CheckIfGrounded(); var dirToGo = Vector3.zero; var rotateDir = Vector3.zero; var dirToGoForwardAction = act[0]; var rotateDirAction = act[1]; var dirToGoSideAction = act[2]; var jumpAction = act[3]; if (dirToGoForwardAction == 1) dirToGo = (grounded ? 1f : 0.5f) * transform.forward * 1f; else if (dirToGoForwardAction == 2) dirToGo = (grounded ? 1f : 0.5f) * transform.forward * volleyballSettings.speedReductionFactor * -1f; if (rotateDirAction == 1) rotateDir = transform.up * -1f; else if (rotateDirAction == 2) rotateDir = transform.up * 1f; if (dirToGoSideAction == 1) dirToGo = (grounded ? 1f : 0.5f) * transform.right * volleyballSettings.speedReductionFactor * -1f; else if (dirToGoSideAction == 2) dirToGo = (grounded ? 1f : 0.5f) * transform.right * volleyballSettings.speedReductionFactor; if (jumpAction == 1) { if (((jumpingTime <= 0f) && grounded)) { Jump(); } } transform.Rotate(rotateDir, Time.fixedDeltaTime * 200f); agentRb.AddForce(agentRot * dirToGo * volleyballSettings.agentRunSpeed, ForceMode.VelocityChange); // makes the agent physically "jump" if (jumpingTime > 0f) { jumpTargetPos = new Vector3(agentRb.position.x, jumpStartingPos.y + volleyballSettings.agentJumpHeight, agentRb.position.z) + agentRot * dirToGo; MoveTowards(jumpTargetPos, agentRb, volleyballSettings.agentJumpVelocity, volleyballSettings.agentJumpVelocityMaxChange); } // provides a downward force to end the jump if (!(jumpingTime > 0f) && !grounded) { agentRb.AddForce(Vector3.down * volleyballSettings.fallingForce, ForceMode.Acceleration); } // controls the jump sequence if (jumpingTime > 0f) { jumpingTime -= Time.fixedDeltaTime; } } ``` ``` public override void Heuristic(in ActionBuffers actionsOut) { var discreteActionsOut = actionsOut.DiscreteActions; if (Input.GetKey(KeyCode.D)) { // rotate right discreteActionsOut[1] = 2; } if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) { // forward discreteActionsOut[0] = 1; } if (Input.GetKey(KeyCode.A)) { // rotate left discreteActionsOut[1] = 1; } if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) { // backward discreteActionsOut[0] = 2; } if (Input.GetKey(KeyCode.LeftArrow)) { // move left discreteActionsOut[2] = 1; } if (Input.GetKey(KeyCode.RightArrow)) { // move right discreteActionsOut[2] = 2; } discreteActionsOut[3] = Input.GetKey(KeyCode.Space) ? 1 : 0; } ``` ![](https://hackmd.io/_uploads/Hku4TUF43.png) 都調整完後就可以開始訓練 ``` behaviors: Volleyball: trainer_type: ppo hyperparameters: batch_size: 2048 buffer_size: 20480 learning_rate: 0.0002 beta: 0.003 epsilon: 0.15 lambd: 0.93 num_epoch: 4 learning_rate_schedule: constant network_settings: normalize: true hidden_units: 256 num_layers: 2 vis_encode_type: simple reward_signals: extrinsic: gamma: 0.96 strength: 1.0 keep_checkpoints: 5 max_steps: 20000000 time_horizon: 1000 summary_freq: 20000 ``` ``` mlagents-learn config/Volleyball.yaml --run-id=V1 http://localhost:6006/ ``` ![](https://hackmd.io/_uploads/SkSpdk9Vh.png) {%youtube QYxTRyxgfjs %} {%youtube yfdTLwsUyKE %} ![](https://i.imgur.com/yMwrppx.png) --- # Adversarial and Cooperative 對抗與合作 將上面2個環境合併,足球和籃球就是基本的範例,不只需要觀察球,同時還要觀察對方的Agent和己方的Agent。 https://community.arm.com/arm-community-blogs/b/graphics-gaming-and-vr-blog/posts/1-unity-ml-agents-arm-game-ai # Behavior Designer ![](https://hackmd.io/_uploads/H10r6y9Nn.png) 獎勵:The mean reward refers to the "Cumulative Reward - The mean cumulative episode reward over all agents. Should increase during a successful training session." While the Std. of reward just describes the standard deviation of the reward which can also be seen as a margin of error. I would recommend to check out some more example environments to understand better how rewards work.