Posts Issued in March, 2025

Pongと強化学習 (93)

posted by sakurai on March 31, 2025 #967

self.env.stepの中でどのようにアクションを判定しているのですか?


self.env.step メソッドの動作を理解するには、まず OpenAI Gym の環境のフレームワークと、具体的にどのようにアクションが環境の状態に適用されるかを考える必要があります。step メソッドは、ゲームやシミュレーションの各ステップで中核となる関数です。この関数は、与えられたアクションを受け取り、そのアクションに基づいて環境の状態を更新し、新しい状態、報酬、エピソードが終了したかどうかのフラグ、および追加情報を返します。

env.step(action) の基本的な動作

  1. アクションの受け取り: 関数はアクションを引数として受け取ります。このアクションは通常、整数(Discrete スペースの場合)、実数のベクトル(Box スペースの場合)、またはその他の形式(MultiDiscrete, MultiBinary など)で表されます。

  2. アクションの適用: 環境はこのアクションを現在のゲームまたはシミュレーションの状態に適用します。アクションが何を意味するか(例えば移動、ジャンプ、射撃など)は、環境の設計に依存します。たとえば、AtariゲームのSpace Invadersでは、アクションには「左に移動」、「右に移動」、「射撃」などが含まれます。

  3. 状態の更新: アクションが適用されると、環境はその結果に基づいて新しい状態を生成します。これには、プレイヤーの位置、敵の位置、スコアなどが含まれる場合があります。

  4. 報酬の計算: 新しい状態に基づいて報酬が計算されます。報酬は、アクションの結果として得られるポイントや、ゲームのルールに基づく他のメトリックによって決定されます。

  5. 終了フラグと追加情報: ゲームが終了したかどうかのフラグ(例えばプレイヤーの命が尽きた、目標を達成した等)と、デバッグや詳細分析に役立つ追加情報が生成されます。

実装の例

以下に、カスタム環境の簡単な step メソッドの実装例を示します。この例では、アクションが環境にどのように適用され、報酬がどのように計算されるかを示しています。

    class CustomEnv(gym.Env):
        def __init__(self):
            super(CustomEnv, self).__init__()
            self.action_space = gym.spaces.Discrete(3)  # 例: 0=左, 1=右, 2=射撃
            self.state = None
            self.done = False
    
        def step(self, action):
            if action == 0:
                # 左に移動する処理
                pass
            elif action == 1:
                # 右に移動する処理
                pass
            elif action == 2:
                # 射撃する処理
                pass
    
            # 状態の更新、報酬の計算、
    
    ゲーム終了のチェック
            reward = 0
            self.done = self.check_if_game_over()
            return self.state, reward, self.done, {}
    
        def reset(self):
            # 環境を初期状態にリセット
            self.state = self.initialize_state()
            self.done = False
            return self.state
    
        def check_if_game_over(self):
            # ゲーム終了条件の確認
            return False
    
        def initialize_state(self):
            # 初期状態の設定
            return None

このように、step メソッドはアクションを受け取り、それに基づいて環境の状態を更新し、結果として新しい状態と報酬を返す役割を果たします。このプロセスはゲームやシミュレーションの種類によって大きく異なる場合がありますが、基本的なフローは同じです。


左矢前のブログ 次のブログ右矢

Pongと強化学習 (92)

posted by sakurai on March 28, 2025 #966

これだと5, 6は移動と射撃が複合されており、移動だけにマイナスのペナルティをつけても射撃につけられたとAIが誤解してしまうので、分離したいのですが。


左右の移動にのみ極小のペナルティを適用するためには、カスタム環境のstepメソッド内で報酬にペナルティを加える処理を追加する必要があります。以下は、CustomSpaceInvadersクラスを修正して、左右の移動に小さなペナルティを適用する方法を示したコードです。

移動アクションへのペナルティの適用

    import gym
    from gym.spaces import MultiBinary
    import numpy as np
    
    class CustomSpaceInvaders(gym.Wrapper):
        def __init__(self, env):
            super(CustomSpaceInvaders, self).__init__(env)
            # アクションスペースを MultiBinary に設定
            # 3つのアクション: [左移動, 右移動, 射撃]
            self.action_space = MultiBinary(3)
            self.move_penalty = -0.01  # 左右移動のペナルティ
    
        def step(self, action):
            # マルチバイナリアクションを元の環境のアクションに変換
            original_action = 0
            if action[0] == 1:  # 左移動
                original_action = 3
            if action[1] == 1:  # 右移動
                original_action = 2
            if action[2] == 1:  # 射撃
                original_action = 1
            if action[0] == 1 and action[2] == 1:  # 左移動 + 射撃
                original_action = 5
            if action[1] == 1 and action[2] == 1:  # 右移動 + 射撃
                original_action = 4
            
            # 環境を進める
            obs, reward, done, info = self.env.step(original_action)
    
            # 左または右に移動した場合にペナルティを適用
            if action[0] == 1 or action[1] == 1:
                reward += self.move_penalty
    
            return obs, reward, done, info
    
    # オリジナルの環境をラップ
    env = gym.make('SpaceInvaders-v0')
    custom_env = CustomSpaceInvaders(env)
    
    # ゲームを開始
    obs = custom_env.reset()
    done = False
    while not done:
        # ランダムなアクションを生成
        action = custom_env.action_space.sample()
        obs, reward, done, info = custom_env.step(action)
        print(f"Action: {action}, Reward: {reward}")
    
    custom_env.close()

コードの解説

  • ペナルティの設定:クラス内にmove_penalty属性を追加し、その値を左右の移動に適用しています。この値は負の小さな数で、移動するたびに報酬から差し引かれます。
  • 報酬の調整:左または右に移動するアクションが選択された場合、rewardにペナルティを加算します(負の値を加算するため、実際には報酬が減少します)。

このようにして、エージェントは左右に移動する際に小さなコストがかかることを学習し、可能であれば移動を避ける戦略をとるようになるかもしれません。ただし、ペナルティの値をどの程度に設定するかは、タスクの性質や学習の目標に応じて慎重に選ぶ必要があります。


左矢前のブログ 次のブログ右矢

Pongと強化学習 (91)

posted by sakurai on March 27, 2025 #965

atariのspace invadersでのenvとのインタフェースで、アクションはどのようにenvに与えていますか?例えばアクションは、左、右、FIREとします。


Atariゲームである「Space Invaders」において、OpenAI Gym 環境を使用する場合、アクションスペースは通常、Discreteタイプで定義されています。この環境では、プレイヤーは一度に1つのアクションのみを実行できますが、そのアクションはプレイヤーが同時に移動と射撃を行うことを可能にするように設計されている場合があります。

Space Invadersのアクションスペース

Space Invadersのアクションセットは次のようになります:

  1. NOOP (何もしない)
  2. FIRE (射撃のみ)
  3. RIGHT (右に移動)
  4. LEFT (左に移動)
  5. RIGHTFIRE (右に移動しながら射撃)
  6. LEFTFIRE (左に移動しながら射撃)

ここで、RIGHTFIRELEFTFIREは移動と射撃を組み合わせたアクションであり、プレイヤーが一度のアクション選択で移動と射撃を同時に実行できることを示しています。

Gym 環境でのアクション実装例

以下の例では、OpenAI Gym の「Space Invaders」環境を初期化し、いくつかのアクションを実行する方法を示します。アクションはランダムに選択されますが、実際の利用時にはエージェントの方策に従って適切なアクションが選択されます。

    import gym
    
    # 環境の作成
    env = gym.make('SpaceInvaders-v0')
    env.reset()
    
    # アクションの意味を表示
    print("Action meanings:", env.unwrapped.get_action_meanings())
    
    # いくつかのステップを実行してみる
    for _ in range(10):
        action = env.action_space.sample()  # ランダムにアクションを選択
        observation, reward, done, info = env.step(action)  # 選択したアクションを環境に適用
        print("Selected action:", action)
        if done:
            env.reset()
    
    env.close()

このコードはSpace Invadersゲームを初期化し、利用可能なアクションを確認し、ランダムにアクションを選択してゲームを進行させます。get_action_meanings()メソッドによりアクションの意味を表示し、実際のゲームプレイでどのようにアクションが適用されるかを観察できます。

まとめ

Space InvadersのようなAtariゲームでは、一般的なDiscreteアクションスペース内で複数の動作を組み合わせるアクション(例えば、移動しながら射撃)を提供することが一般的です。これにより、プレイヤーはより戦略的にゲームを進めることが可能になります。


左矢前のブログ 次のブログ右矢

posted by sakurai on March 24, 2025 #964

AJEに依頼したアブストラクトの修正が返ってきたので反映し、システムに登録しました。今回のIDは#17で、採択の発表は6月2日とのことです。

表964.1 RAMS 2026へのマイルストーン
期限 マイルストーン 状態
2025/4/30 AJEにアブストラクトを修正依頼
2025/4/30 アブストラクト(氏名、所属無し版)投稿締め切り(システム入力)
2025/6/2 アブストラクト採択結果
2025/7/31 初稿論文、プレゼン投稿締め切り(氏名、所属無し版)
2025/9/31 最終論文、プレゼン投稿締め切り(氏名、所属有り版)
2025/10/10 学会出席登録締め切り


左矢前のブログ 次のブログ右矢

Pongと強化学習 (90)

posted by sakurai on March 21, 2025 #963

TbのBSVソースを示します。

Tb.bsv:

    import StmtFSM::*;
    import DumpFSM::*;
    import Uart::*;
    import RegFile::*;
    import Connectable::*;
    
    typedef Bit#(16) Addr;
    typedef Bit#(4) Data;
    
    `define STX 8'h02
    
    (* synthesize *)
    module mkTb();
       RegFile#(Addr, Data) vram <- mkRegFileLoad("data.hex", 0, 256*256-1);
       Reg#(Bool) cwait <- mkReg(False);
       Reg#(Bool) game <- mkReg(True);
       Reg#(Bool) tic <- mkReg(False);
         
       DumpFSM_ifc dump <- mkDumpFSM();
       Uart_ifc uart <- mkUart();
       
       mkConnection(cwait, dump.cwaitm);
       mkConnection(dump.datam, vram.sub(dump.addrm));
       mkConnection(tic, dump.c60Hz);
       mkConnection(game, dump.mode);
       mkConnection(uart.sout, dump.sin);
       mkConnection(uart.sin, dump.sout);
    
       Stmt test =
       seq
          // Game Mode: ticをスルーする
          game <= True;
          cwait <= False;
          repeat(100) noAction;
          // 非Game Mode: GameFSMに停止要求を出して停止したらダンプする
          game <= False;
          uart.write(8'h31);
          uart.write(8'h32);
          uart.write(8'h33);
          uart.write(`STX); // ここからダンプスタート
          uart.write(8'h31);
          uart.write(8'h32);
          uart.write(8'h33);
          cwait <= True; // GameFSMが停止したシミュレーション
          repeat(20) uart.write(8'h31);
          repeat(100) noAction;
          game <= True;
          await(!dump.selm()); // DumpFSMが動作完了し、SELをOFF
          repeat(100) noAction;
          $finish;
       endseq;
    
       mkAutoFSM(test);
    
        // クロックのトグルをカウントするレジスタ
        Reg#(Bit#(4)) counter <- mkReg(0);
        // カウンターが特定の値に達したらクロックをトグル
        rule toggleClock;
            if (counter == 5) begin // ここで分周比を調整
                tic <= !tic;
                counter <= 0;
            end else begin
                counter <= counter + 1;
            end
        endrule
        
    endmodule

左矢前のブログ 次のブログ右矢

Pongと強化学習 (89)

posted by sakurai on March 20, 2025 #962

BSVにより、DumpFSMを改造します。従来のDumpDSMの仕様は、

  • SW等の外部指示により、GameFSMにウエイト要求を出す
  • GameFSMはSyncWait時にウエイト信号(停止中信号)を出力する
  • DumpFSMは停止中信号を確認し、SELをTrueとする
  • VRAMの内容をUARTで出力する
  • 完了したらSELとウエイト要求をFalseにする

という動作でした。今回改造するポイントは外部指示ではなく、強化学習ソフトウエアの指示により画面ダンプを実施する点です。そのために従来送信部のみであったUARTを受信部も作成します。

  • SW等の外部指示により、GameモードがFalseになる
  • シリアルデータがSTXを指示したらGameFSMにwait要求を出す
  • GameFSMはSyncWait時にウエイト信号(停止中信号)を出力する
  • DumpFSMは停止中信号を確認し、SELをTrueとする
  • SELがTrueの間は60Hz信号を遮断する。これによりGameFSMが進むことは無い
  • VRAMの内容をUARTで出力する
  • 完了したらSELとウエイト要求をFalseにする。
  • GameモードがTrueになったら60Hz信号を再開する。

図962.1に構造図を示します。DumpFSMモジュールには送信と受信のUARTを配置し、それをテストするテストベンチ内にも同じUARTを対向して配置します。

図%%.1
図962.1 ブロック図

DumpFSM.bsv

    import StmtFSM::*;
    import Uart::*;
    import Connectable::*;
    
    typedef Bit#(16) Addr;
    typedef Bit#(4) Data;
    typedef Bit#(8) Byte;
    
    `define STX 8'h02
    
    interface DumpFSM_ifc;
       (* prefix="" *)
       method Action datam(Data newdata); // input
       (* prefix="" *)
       method Action cwaitm(Bool newcwait); // input
       method Bool sreqm(); // output
       method Addr addrm(); // output
       method Bool selm(); // output
       method Bit#(1) sout(); // serial output
       (* prefix="" *)
       method Action sin(Bit#(1) nidata); // serial input
       (* prefix="" *)
       method Action c60Hz(Bool newtic); // input
       method Bool ticfunction(); // output
       (* prefix="" *)
       method Action mode(Bool newgame); // input
    endinterface
    
    (* synthesize, always_ready="sreqm, addrm, selm, sout, ticfunction",
                   always_enabled="datam, cwaitm, sin, c60Hz, mode" *)
    module mkDumpFSM(DumpFSM_ifc);
    
       Reg#(Addr) addr <- mkReg(0);
       Reg#(Data) data <- mkRegU;
       Reg#(Byte) byteData <- mkRegU;
       Reg#(UInt#(9)) x <- mkRegU;
       Reg#(UInt#(9)) y <- mkRegU;
       Reg#(UInt#(4)) i <- mkRegU;
       Reg#(Bool) cwait <- mkReg(False);
       Reg#(Bool) sreq <- mkReg(False);
       Reg#(Bool) sel <- mkReg(False);
       Reg#(Bool) tic <- mkReg(False);
       Reg#(Bool) game <- mkReg(True);
       
       Uart_ifc uart <- mkUart();
    
       function Stmt nibbleOut(Data nibble);
          return (seq
             byteData <= extend(nibble) + ((nibble >= 10) ? (-10 + 8'h61) : 8'h30);
             uart.write(byteData);
             $write("%c", byteData);
          endseq);
       endfunction: nibbleOut
    
       Stmt test = seq
           while (True) seq
               await(!game); // Gameでない場合、トリガが来たら吸出し
               await(uart.read() == `STX);
               sreq <= True; // 描画マスタ停止要求を出す
               await(cwait); // 描画マスタ停止したら、
               sel <= True; // ダンプ開始
               for (i <= 1; i <= 4; i <= i + 1) seq
                   uart.write(8'h38); // start of header '8888' -> remove
                   $display("8");
               endseq
               addr <= 0;
               for (y <= 0; y <= 2/* 255 */; y <= y + 1) seq
                   for (x <= 0; x <= 255; x <= x + 1) seq
    //                 $display("%3d, %3d", x, y);
    //                 $display(" %04x ", addr);
                       action
                          addr <= addr + 1;
                       endaction
                       nibbleOut(data);
                   endseq // for x
                   uart.write(8'h0a); // LF
               endseq // for y
               await (uart.done()); // 送信終了待ち
               action
                   sreq <= False; // 要求OFF
                   sel <= False; // 選択OFF
               endaction
          endseq // while
       endseq; // Stmt
    
       mkAutoFSM(test);
    
       method Bool sreqm(); // output
          return sreq;
       endmethod
       method Addr addrm(); // output
          return addr;
       endmethod
       method Action datam(Data newdata); // input
          data <= newdata;
       endmethod
       method Action cwaitm(Bool newcwait); // input
          cwait <= newcwait;
       endmethod
       method Bool selm(); // output
          return sel;
       endmethod
       method Bit#(1) sout(); // serial output
          return uart.sout();
       endmethod
       method Action sin(Bit#(1) nidata); // serial input
          action
             uart.sin(nidata);
          endaction
       endmethod
       method Action c60Hz(Bool newtic); // input
          if (game && !sel) action
             tic <= newtic;
          endaction
       endmethod
       method Bool ticfunction; // output
          return tic;
       endmethod
       method Action mode(Bool newgame); // input
          game <= newgame;
       endmethod
    
    endmodule

左矢前のブログ 次のブログ右矢

Pongと強化学習 (88)

posted by sakurai on March 19, 2025 #961

uartからの通信データをboxの配列に格納するにはどうしますか?


UART(Universal Asynchronous Receiver/Transmitter)からの通信データをOpenAI GymのBox型の配列に格納するプロセスは、いくつかのステップに分けて行うことができます。これは、シリアル通信を介してデバイス(例えば、FPGAやセンサー)からデータを受け取り、それをPythonで処理してOpenAI Gymの環境で使用できる形式に変換するための方法です。

ステップ 1: UART通信のセットアップ

まず、Pythonでシリアルポートを開いてUART通信を行うための準備をします。これには通常、pyserialライブラリを使用します。

    import serial
    
    # シリアルポートの設定
    ser = serial.Serial('/dev/ttyUSB0', baudrate=115200, timeout=1)  # 適切なポートとボーレートを指定

ステップ 2: データの受信と解析

UARTから送られてくるデータを読み取り、必要に応じて解析します。データの形式は事前にデバイスと合意しておく必要があります(例えば、バイト列、テキストフォーマット、特定の区切り文字で区切られた値など)。

    data = ser.read(10)  # データを読み込むバイト数を指定
    parsed_data = [float(val) for val in data.split()]  # データを解析し、リストに格納

ステップ 3: データをBoxに格納

受け取ったデータをBox型の配列として格納します。このBoxは、受け取るデータの範囲や形状に応じて定義します。

    import numpy as np
    from gym import spaces
    
    # Boxの定義。ここでは例として、受け取るデータが3次元の浮動小数点数と仮定
    observation_space = spaces.Box(low=-np.inf, high=np.inf, shape=(3,), dtype=np.float32)
    
    # UARTから受け取ったデータをNumpy配列に変換
    observation = np.array(parsed_data, dtype=np.float32)

ステップ 4: Gym環境での使用

observation配列は、これでOpenAI Gymの環境内で使用する観測値として利用可能です。環境内でこの観測値をエージェントに提供することができます。

    # エージェントに観測値を提供する例
    observation = env.step(action)  # エージェントからのアクションを受けて、次のステップを実行

この一連のステップにより、UARTからの生の通信データを処理し、OpenAI GymのBox型の観測空間に適合する形で整形して使用することができます。データの形式や具体的な処理は、接続するデバイスやその用途によって異なるため、これらのステップは適宜調整が必要です。


左矢前のブログ 次のブログ右矢

posted by sakurai on March 13, 2025 #960

AJEに依頼した論文のアブストラクトが返ってきたので、修正し再依頼しました。アブストラクトなのでそれほどやりとりなく、次で完了する予定です。

表960.1 RAMS 2026へのマイルストーン
期限 マイルストーン 状態
2025/4/30 AJEにアブストラクトを修正依頼 修正中
2025/4/30 アブストラクト(氏名、所属無し版)投稿締め切り(システム入力)
2025/6/2 アブストラクト採択結果
2025/7/31 初稿論文、プレゼン投稿締め切り(氏名、所属無し版)
2025/9/31 最終論文、プレゼン投稿締め切り(氏名、所属有り版)
2025/10/10 学会出席登録締め切り


左矢前のブログ 次のブログ右矢

posted by sakurai on March 12, 2025 #959

次回RAMS 2026に投稿する論文「Optimizing Fault Tree Analysis for Probabilistic Metrics Using Large Language Models in ISO 26262」を作成しました。アブストラクトをAJEに投稿中です。

表959.1 RAMS 2026へのマイルストーン
期限 マイルストーン 状態
2025/4/30 AJEにアブストラクトを投稿
2025/4/30 アブストラクト(氏名、所属無し版)投稿締め切り(システム入力)
2025/6/2 アブストラクト採択結果
2025/7/31 初稿論文、プレゼン投稿締め切り(氏名、所属無し版)
2025/9/31 最終論文、プレゼン投稿締め切り(氏名、所属有り版)
2025/10/10 学会出席登録締め切り


左矢前のブログ 次のブログ右矢

Pongと強化学習 (87)

posted by sakurai on March 6, 2025 #958

envの内部画面配列は実際はどのように定義されていますか?


envの内部画面配列、つまり観測空間は、OpenAI Gym 環境によって提供される状態(観測)のデータ構造です。これは、エージェントが受け取る環境の状態を表現します。具体的な定義はゲームやシミュレーションの種類によって異なりますが、通常、以下のような形式が多く見られます:

1. オリジナルの画像データ

ビデオゲームなどの場合、オリジナルの画像データはしばしばRGBカラー画像として提供されます。たとえば、Atariゲームの一般的な解像度は210x160ピクセルで、各ピクセルはRGB値を持ちます。この形式の観測空間はBox型で定義され、以下のように設定されることが一般的です:

    from gym import spaces
    observation_space = spaces.Box(low=0, high=255, shape=(height, width, channels), dtype=np.uint8)
  • lowhighはピクセル値の最小値と最大値を示し、通常RGBの各色成分は0から255の値を取ります。
  • shapeは画像の次元を指定します。例えば、高さが160、幅が210、3つのカラーチャネル(RGB)を持つ場合、(160, 210, 3)となります。
  • dtype=np.uint8はデータ型を8ビットの符号なし整数と指定しています。

2. 加工後の画像データ

強化学習のための前処理ステップを経て、画像データはしばしば変更されます。例として、画像をグレースケールに変換し、サイズを変更し、複数のフレームをスタックすることがあります。これにより、観測空間は次のように定義されることがあります:

    observation_space = spaces.Box(low=0, high=255, shape=(84, 84, 4), dtype=np.uint8)
  • shapeの変更により、画像サイズが84x84にリサイズされ、4つの連続するフレームがスタックされています。これにより、エージェントは動きの情報を時間的に捉えることができます。

3. その他の形式

特定のシミュレーションや環境では、観測空間が画像データではなく、センサー読み取り値や他の数値データを直接利用する場合もあります。例えば、ロボティクスのシミュレーションでは、関節の位置、速度、角度などの物理的なパラメータが観測空間として使用されることがあります。

このように、観測空間はエージェントが操作する環境の状態をエンコードするためのデータ構造として、その内容は非常に多様であり、エージェントの設計や学習タスクに適応する形で設定されます。


左矢前のブログ 次のブログ右矢


ページ: