ML-Agents를 사용한 강화 학습으로 Boids 모도키
결과가 이쪽.
강화 학습의 학습 단계를 Youtube에 올려 보았습니다. 점점 Boids 같아졌습니다. htps // t. 코/8HphXqT9lq — foka (@foka22ok) 2018년 9월 7일
수십만 스텝 정도부터 그런 움직임이 되어 온 것 같아요.
학습 조건
다음 조건에서 학습했습니다.
· 정면으로 일정 속도로 진행
・붉은 구슬의 일정 거리 내에 가까워지면 최대 플러스 보상
・붉은 구슬의 방향을 향할수록 플러스 보상
· 몸이 뒤집히지 않으면 플러스 보상
・정면 상하 좌우의 근거리에 다른 물고기가 있으면 마이너스 보상
Brain과 Agent
Brain
※당초는 어군이 아니고, 비행체로 하려고 하고 있었으므로 FlyingBrain이라고 명명해 버렸습니다...
학습으로 취급하는 상태
GameObject target;//追いかけるターゲット
GameObject enemy;//避ける敵(使わなかった)
public override void CollectObservations()
{
AddVectorObs(transform.position); //自身の位置
AddVectorObs(transform.rotation); //自身の向き
AddVectorObs(target.transform.position - transform.position); //ターゲットまでの相対位置
AddVectorObs(enemy.transform.position- transform.position); //敵までの相対位置(使わなかった)
AddVectorObs(otherDistance); //正面上下左右の一定距離内の他の仲間との距離
}
※enemy는, 당초 적 캐릭터를 두고 피하도록(듯이) 하려고 생각하고 있던 명잔.
다음의 파라미터 수를 합하면 Brain의 Space Size에서 설정한 18과 같은 수가 됩니다.
Agent의 AddVectorObs 매개 변수 수는 Brain의 Space Size와 같아야 합니다.
· 자신의 위치 x, y, z
· 자신의 방향 x, y, z, w
· 타겟까지의 상대 위치 x, y, z
・적까지의 상대 위치 x,y,z
・정면 상하 좌우의 다른 동료와의 거리 float[5]
에이전트 행동
AgentAction: MLAgents.Agent 기능
Rigidbody rb;
public override void AgentAction(float[] vectorAction, string textAction)
{
float speed = 4f;
rb.velocity = speed * transform.forward;//正面に一定速度で進む
float actionX = speed * Mathf.Clamp(vectorAction[0], -1f, 1f); //左右回転情報として扱った
float actionZ = speed * Mathf.Clamp(vectorAction[1], -1f, 1f); //上下回転情報として扱った
//ここで強化学習の制御を反映している。今回は上下左右の回転制御にのみ利用。
transform.rotation = transform.rotation* Quaternion.Euler(new Vector3(actionZ, actionX, 0));
CheckOtherDistance(); //正面上下左右に他の仲間がいるか調べる。下記参照
SetReward(); //条件に応じて報酬を設定する。下記参照
}
다른 동료와의 거리 확인
Rigidbody rb;
public override void AgentAction(float[] vectorAction, string textAction)
{
float speed = 4f;
rb.velocity = speed * transform.forward;//正面に一定速度で進む
float actionX = speed * Mathf.Clamp(vectorAction[0], -1f, 1f); //左右回転情報として扱った
float actionZ = speed * Mathf.Clamp(vectorAction[1], -1f, 1f); //上下回転情報として扱った
//ここで強化学習の制御を反映している。今回は上下左右の回転制御にのみ利用。
transform.rotation = transform.rotation* Quaternion.Euler(new Vector3(actionZ, actionX, 0));
CheckOtherDistance(); //正面上下左右に他の仲間がいるか調べる。下記参照
SetReward(); //条件に応じて報酬を設定する。下記参照
}
그룹의 수를 나중에 늘리거나 줄일 수 있기를 원했기 때문에 모든 동료의 위치는 모니터링하지 않고 Physics.Raycast에서 일정 거리 내에 반응이 있는지 여부만 모니터링했습니다.
[SerializeField]
LayerMask mask;//チェック対象レイヤーを限定しておく
void CheckOtherDistance() {
//初期化
for(int i=0;i<otherDistance.Length; i++){
otherDistance[i] = 0;
}
float rayDistance = 2.0f;
//正面チェック
Ray ray = new Ray(transform.position, transform.forward);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, rayDistance, mask)) {
otherDistance[0] = hit.distance;
}
//右方向チェック
ray.direction = transform.right;
if (Physics.Raycast(ray, out hit, rayDistance, mask)) {
otherDistance[1] = hit.distance;
}
//左、上、下もPhysics.Raycastで同様にチェック
}
보상 설정
SetReward: 자체 제작 함수
int flyCnt=0;//移動カウント。
float targetDist;//ターゲットとの距離。前フレームと比較して近づいているかどうかの判定に使う
void SetReward() {
flyCnt++;
//Bodyがひっくり返ってないかチェック。姿勢がちゃんとしてればより高報酬。
float rDistance = Vector3.Distance(new Vector3(0,1,0), transform.up.normalized);
if (rDistance < 1) {
AddReward((1 - rDistance) * 0.01f);
}
//ターゲットを向いているかチェック。ターゲットを向いていればいるほど高報酬。
Vector3 targetVector = (target.transform.position - transform.position).normalized;
float vDistance = Vector3.Distance(targetVector, transform.forward);
AddReward((1- vDistance) * 0.05f);
float nowDist = Vector3.Distance(target.transform.position, transform.position);
//前フレームよりターゲットに近づけば微報酬。もしかしたらなくても良かったかもしれない
if (nowDist < targetDist) {
AddReward(0.01f);
}
//flyCnt が一定以上経った状態で離れたところにいる場合は移動失敗とみなして最大マイナス報酬してDone!。
else if (flyCnt > 300 && nowDist > 150) {
AddReward(-1);
Done();
}
targetDist = nowDist;
//ターゲットに最接近したらゴールとして最大報酬してDone!。
if (nowDist < 1f) {
AddReward(1f);
Done();
}
//正面の上下左右に他の仲間がいたらマイナス報酬。raycastで反応があればotherDist が0より大きくなるようにしているので0より大きいかどうかだけで判定した。
foreach (float otherDist in otherDistance) {
if (otherDist > 0) {
AddReward(-0.01f);
}
}
}
int flyCnt=0;//移動カウント。
float targetDist;//ターゲットとの距離。前フレームと比較して近づいているかどうかの判定に使う
void SetReward() {
flyCnt++;
//Bodyがひっくり返ってないかチェック。姿勢がちゃんとしてればより高報酬。
float rDistance = Vector3.Distance(new Vector3(0,1,0), transform.up.normalized);
if (rDistance < 1) {
AddReward((1 - rDistance) * 0.01f);
}
//ターゲットを向いているかチェック。ターゲットを向いていればいるほど高報酬。
Vector3 targetVector = (target.transform.position - transform.position).normalized;
float vDistance = Vector3.Distance(targetVector, transform.forward);
AddReward((1- vDistance) * 0.05f);
float nowDist = Vector3.Distance(target.transform.position, transform.position);
//前フレームよりターゲットに近づけば微報酬。もしかしたらなくても良かったかもしれない
if (nowDist < targetDist) {
AddReward(0.01f);
}
//flyCnt が一定以上経った状態で離れたところにいる場合は移動失敗とみなして最大マイナス報酬してDone!。
else if (flyCnt > 300 && nowDist > 150) {
AddReward(-1);
Done();
}
targetDist = nowDist;
//ターゲットに最接近したらゴールとして最大報酬してDone!。
if (nowDist < 1f) {
AddReward(1f);
Done();
}
//正面の上下左右に他の仲間がいたらマイナス報酬。raycastで反応があればotherDist が0より大きくなるようにしているので0より大きいかどうかだけで判定した。
foreach (float otherDist in otherDistance) {
if (otherDist > 0) {
AddReward(-0.01f);
}
}
}
처음에는 몸이 뒤집혀 있는지 확인하지 않았지만, 그것이라고 뒤집은 채로도 신경쓰지 않고 진행하는 녀석이 다발....
몸의 자세에 따라 보상을 설정하면 나름대로 잘 작동했습니다.
AgentReset시
AgentReset: MLAgents.Agent 기능
public override void AgentReset()
{
//AgentResetの際に、位置と向きをランダムに指定して、どの向き・どの位置からでも対応できるようにした
float positionRange = 20f;
float angleRange = 180f;
flyCnt = 0;
transform.position = new Vector3(Random.Range(-positionRange, positionRange), Random.Range(-positionRange, positionRange), Random.Range(-positionRange, positionRange));
transform.rotation = Quaternion.Euler(new Vector3(Random.Range(-angleRange, angleRange), Random.Range(-angleRange, angleRange), Random.Range(-angleRange, angleRange)));
targetDist = Vector3.Distance(target.transform.position, transform.position);
}
요약
public override void AgentReset()
{
//AgentResetの際に、位置と向きをランダムに指定して、どの向き・どの位置からでも対応できるようにした
float positionRange = 20f;
float angleRange = 180f;
flyCnt = 0;
transform.position = new Vector3(Random.Range(-positionRange, positionRange), Random.Range(-positionRange, positionRange), Random.Range(-positionRange, positionRange));
transform.rotation = Quaternion.Euler(new Vector3(Random.Range(-angleRange, angleRange), Random.Range(-angleRange, angleRange), Random.Range(-angleRange, angleRange)));
targetDist = Vector3.Distance(target.transform.position, transform.position);
}
손 탐구로 시행착오하면서 해 보았으므로, 이상한 일도 해 그렇습니다만, 일단 이것으로 Boids같이 되었습니다.
아주 좋으면 보통 Boids 구현하는 것보다 처리가 가볍으면 좋겠다, 라고 기대했지만, 강화 학습 어떻게 이렇게 말하기 전에, Raycast가 무거웠습니다....
나름의 결과를 내기 위해서 학습 시간이 수십분에서 1시간 정도 걸리는 일도 있어, 조금 조정해 확인한다, 라고 하는 것만으로도 상당한 시간이 걸려 마음이 부러질 것 같게 되었습니다.
그래도, 우선은, 어쩐지 ML-Agents의 사용법을 알았기 때문에 좋다고 합니다.
Reference
이 문제에 관하여(ML-Agents를 사용한 강화 학습으로 Boids 모도키), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/foka22ok/items/808aec22e5b5d90810df텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)