Posts Tagged with "FPGA"

既に発行済みのブログであっても適宜修正・追加することがあります。
We may make changes and additions to blogs already published.

Pongと強化学習 (48)

posted by sakurai on July 29, 2024 #843

ビデオゲームだとシーケンスが重要そうなので、それを質問してみます。


シーケンスや時間的な文脈が重要な強化学習の問題では、経験リプレイを用いたランダムな経験の取り出しは確かに課題となる可能性があります。特に、タスクが一連の行動の順序や特定の時間的パターンを重視する場合(例えば、ビデオゲームの特定のレベルをクリアする動作や、複雑な戦略が必要なボードゲーム)、ランダムにサンプルされた経験はそれらの重要な時間的関係性を損なう可能性があります。

課題

  • 時間的関係性の喪失: ランダムに経験を取り出すと、元のエピソード内での行動の順序や因果関係が失われ、エージェントが正確なポリシーを学習するのを難しくする可能性があります。
  • クレジット割り当て問題: 報酬は通常、シーケンスの終わりに近い行動に関連して得られるため、どの行動が最終的な報酬に貢献したかを正確に学習することが困難になる場合があります。

解決策

このような問題に対処するために、いくつかのアプローチが提案されています。

  • nステップリターンの使用: 即時報酬だけでなく、将来のnステップ分の報酬も考慮に入れることで、報酬の原因となる行動との間の時間的関係性を保持します。
  • Prioritized Experience Replay: 全ての経験を等しく扱うのではなく、より「重要な」経験から学習することを優先します。ここでの「重要性」は、TD誤差の大きさなどに基づいて決定されることが一般的です。
  • Recurrent Neural Network (RNN) の利用: RNNやその変種(LSTMやGRUなど)は、入力データのシーケンスを扱う能力に優れているため、時間的な文脈を保持するために使用されることがあります。

これらの技術を利用することで、時間的な文脈を重視するタスクにおいても、経験リプレイの利点を活かしつつ、シーケンスの情報を効果的にエージェントが学習することが可能になります。


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

Pongと強化学習 (47)

posted by sakurai on July 26, 2024 #842

具体的にバッチとはどのような情報なのかを聞いてみます。


リプレイメモリから抽出されるバッチは、エージェントが環境との相互作用から得た経験の集まりです。強化学習の文脈において、「経験」とは、ある時点での状態(state)、その状態で取られた行動(action)、その行動によって得られた報酬(reward)、および行動の結果として遷移した次の状態(next state)の組み合わせを指します。リプレイメモリからのバッチ抽出により、これらの経験をミニバッチ学習に利用します。

バッチの構成要素

一般的に、バッチは以下の4つの要素から構成されます。

  1. 状態(States): エージェントが経験した、ある時点での環境の状態です。状態は、画像(ピクセルデータ)、センサーの読み取り値、ゲームのスコアなど、環境に応じた任意の形式で表されることがあります。
  2. 行動(Actions): エージェントが状態において選択した行動です。行動は、移動する方向、アイテムの使用、攻撃の実行など、特定の状態におけるエージェントの選択を反映します。
  3. 報酬(Rewards): 行動を取った結果としてエージェントが受け取った報酬です。報酬は、行動の結果がエージェントにとって有利か不利かを示す数値で、通常はスカラー値です。
  4. 次の状態(Next States): 行動の結果エージェントが遷移した次の状態です。これは、行動後の環境の新しい状態を示し、学習プロセスにおいて重要な役割を果たします。

バッチ学習のプロセス

リプレイメモリからランダムにサンプルされたバッチは、ニューラルネットワークの学習に利用されます。このプロセスは以下のように進行します。

  • バッチサイズの選択: リプレイメモリから一度に取り出す経験の数を決定します。バッチサイズは、学習の効率と性能に影響を与える重要なハイパーパラメータです。
  • バッチ抽出: リプレイメモリからランダムに選択された経験の集合を抽出します。このランダム性により、学習データの相関を減少させ、過学習を防ぐ効果があります。
  • 学習: 抽出されたバッチを使用して、エージェントの行動価値関数(Q関数など)を近似するニューラルネットワークの重みを更新します。この更新には、通常、誤差逆伝播アルゴリズムと勾配降下法が使用されます。

バッチ学習により、エージェントはリプレイメモリに蓄積された経験から学習を行い、より優れたポリシーに徐々に収束していくことが期待されます。


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

Pongと強化学習 (46)

posted by sakurai on July 25, 2024 #841

ターゲット$Q$ネットワークを深掘りしてみます。


ターゲットQネットワークは、Deep Q-Network (DQN) アルゴリズムにおいて、学習の安定性を向上させるために導入される重要な概念です。DQNでは、行動価値関数(Q関数)を近似するために深層ニューラルネットワークが使用されますが、この学習プロセスは、特に高次元の状態空間を扱う場合に不安定になりがちです。ターゲットQネットワークは、この問題に対処するために設計されています。

ターゲットQネットワークの役割

ターゲットQネットワークは、主Qネットワークのコピーであり、学習中にQ値の更新ターゲットとして使用されます。主Qネットワークは学習の各ステップで更新されるのに対し、ターゲットQネットワークは定期的にのみ更新されます(例えば、数百ステップごと)。この遅延更新により、学習ターゲットが一定期間固定され、学習プロセスの安定性が向上します。

ターゲットQネットワークの実装の概要

  1. ネットワークの初期化: 学習を開始する際、主QネットワークとターゲットQネットワークの両方を同じ重みで初期化します。
  2. 経験リプレイ: エージェントの経験(状態、行動、報酬、次の状態)をリプレイメモリに保存します。
  3. 学習バッチの抽出: リプレイメモリからランダムにバッチを抽出します。
  4. Q値の更新: 抽出したバッチに基づき、主Qネットワークを使用して現在のQ値を計算し、ターゲットQネットワークを使用してターゲットQ値を計算します。次に、これらのQ値の差(TD誤差)に基づいて、主Qネットワークの重みを更新します。
  5. ターゲットQネットワークの更新: 定期的に主Qネットワークの重みをターゲットQネットワークにコピーします。

コードスニペット(疑似コード)

# 主QネットワークとターゲットQネットワークの初期化
Q_network = initialize_network()
target_Q_network = copy_network(Q_network)

# 学習ループ
for step in range(num_steps):
    # 経験リプレイからバッチを抽出
    batch = replay_memory.sample(batch_size)
    
    # ターゲットQ値の計算
    target_Q_values = calculate_target_Q_values(batch, target_Q_network)
    
    # 主Qネットワークの更新
    update_Q_network(Q_network, batch, target_Q_values)
    
    # 定期的にターゲットQネットワークを更新
    if step % update_target_network_steps == 0:
        target_Q_network = copy_network(Q_network)

ターゲットQネットワークを使用することで、学習中の目標値が頻繁に変動することを防ぎ、DQNの学習プロセスをより安定させることができます。この安定化は、特に複雑な環境やタスクにおいて、学習の収束を助ける重要な要素となります。


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

Pongと強化学習 (45)

posted by sakurai on July 24, 2024 #840

経験リプレイのBSVコードを要求してみます。


経験リプレイ(Experience Replay)は、強化学習アルゴリズムにおいて学習データを効率的に利用し、学習の安定性を向上させるために使用されます。直接的なBSV(Bluespec SystemVerilog)コードを提供することはできませんが、経験リプレイを実装する際の基本的なアイデアを説明し、疑似コードを通じてその概念を示します。

経験リプレイのプロセスは、主に以下のステップから成り立っています:

  1. エージェントが環境との相互作用から得た経験(状態、行動、報酬、次の状態)を保存します。
  2. 学習時に、保存された経験の中からランダムにサンプル(ミニバッチ)を選び出して学習に利用します。

疑似コード

以下は経験リプレイを実装するための疑似コードです。このコードはあくまで一般的なプログラミング言語に近い形で記述されており、BSVの構文とは異なります。

# 経験リプレイバッファの定義
class ExperienceReplayBuffer:
    def __init__(self, capacity):
        self.capacity = capacity  # バッファの最大容量
        self.buffer = []  # 経験を保存するリスト
        self.position = 0  # 現在の挿入位置

    # 経験をバッファに追加
    def push(self, state, action, reward, next_state):
        if len(self.buffer) < self.capacity:
            self.buffer.append(None)  # バッファが最大容量に達していなければ拡張
        self.buffer[self.position] = (state, action, reward, next_state)
        self.position = (self.position + 1) % self.capacity  # 循環的に位置を更新

    # バッファからランダムにミニバッチを抽出
    def sample(self, batch_size):
        return random.sample(self.buffer, batch_size)

    # バッファの現在のサイズを返す
    def __len__(self):
        return len(self.buffer)

# 経験リプレイバッファの使用例
replay_buffer = ExperienceReplayBuffer(capacity=10000)
# エージェントの経験(状態、行動、報酬、次の状態)を追加
replay_buffer.push(state, action, reward, next_state)
# 学習に使用するためのミニバッチをバッファから抽出
batch = replay_buffer.sample(batch_size=32)

この疑似コードは、経験リプレイバッファの基本的な実装を示しています。バッファは、エージェントが収集した経験を保存するためのもので、学習時にはこのバッファからランダムに経験のミニバッチを抽出して使用します。

BSVでの具体的な実装には、この概念を基にBSVの言語特性に合わせて設計を行う必要があります。データ構造の管理、ランダムアクセス、および並列データ処理など、ハードウェア記述言語の特性を考慮した実装が求められます。


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

Pongと強化学習 (44)

posted by sakurai on July 23, 2024 #839

経験リプレイを深掘りしてみます。


経験リプレイ(Experience Replay)は、深層強化学習における重要なテクニックの一つで、特にDeep Q-Network (DQN) などのアルゴリズムで利用されます。この方法は、エージェントが環境との相互作用から得た経験をメモリに保存し、学習の際にそのメモリからランダムにサンプルを抽出して再利用するというものです。

経験リプレイの目的

経験リプレイの主な目的は、学習プロセスの効率と安定性を向上させることです。具体的には、以下のような利点があります。

  • 非独立同分布(IID)問題の緩和: 強化学習では、エージェントの行動が次の状態に影響を与えるため、連続した経験は高い相関を持ちます。経験リプレイにより、過去の経験をランダムにサンプルすることで、これらの相関を減少させ、学習データがIIDに近くなるようにします。
  • 効率的な学習: 同じ経験を複数回学習に使用することで、得られたデータから最大限の学習効果を得られます。
  • バリアンスの減少: 経験のランダムサンプリングにより、特定の状況や行動に対する過剰な適合を防ぎ、学習プロセスのバリアンスを減少させます。

経験リプレイの実装

経験リプレイの実装には、主に以下のステップが含まれます。

  1. 経験の保存: エージェントが環境と相互作用する際に、状態(s)、行動(a)、報酬(r)、次の状態(s')、およびエピソードが終了したかどうかを示すフラグをメモリに保存します。
  2. リプレイメモリ: 保存された経験はリプレイメモリ(あるいはリプレイバッファ)と呼ばれるデータ構造に格納されます。このメモリは一定のサイズを持ち、容量を超えた場合は最も古い経験から順に削除されます。
  3. ミニバッチ学習: 学習の際には、リプレイメモリからランダムに一定数の経験をサンプルとして抽出し、これらのミニバッチを用いてニューラルネットワークのパラメータを更新します。

経験リプレイの利用

経験リプレイは、特に大規模な状態空間や行動空間を持つ問題、またはサンプル効率が重要なアプリケーションにおいて、強化学習アルゴリズムの性能向上に貢献します。この技術により、データの再利用と学習の安定化を図ることが可能になり、深層強化学習の成功事例の一つとなっています。


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

Pongと強化学習 (43)

posted by sakurai on July 22, 2024 #838

TD誤差について聞いてみます。


Deep Q-Network (DQN) の実装では、行動価値関数$Q(s, a)$を使用して学習を行います。DQNは、各状態$s$で取り得る各行動$a$に対する期待報酬を推定することに焦点を当てたアルゴリズムで、強化学習の問題を解決するためにニューラルネットワークを利用します。

TD誤差のDQNにおける役割

DQNにおいて、TD誤差は次のように行動価値関数$Q$の差として計算されます:

$$ TD_{t} = R_{t+1} + \gamma \max_{a'} Q(S_{t+1}, a') - Q(S_t, A_t) $$

ここで、

  • $R_{t+1}$は時刻$t$での行動$A_t$に対する即時報酬です。
  • $\gamma$は割引率で、将来の報酬を現在の価値にどの程度割り引くかを示します。
  • $Q(S_{t+1}, a')$は次の状態$S_{t+1}$で取り得るすべての行動$a'$に対する$Q$値の最大値です。
  • $Q(S_t, A_t)$は現在の状態$S_t$と行動$A_t$に対する$Q$値です。

このTD誤差は、$Q$関数の現在の推定値と、観測された報酬に基づいて更新された$Q$値の推定値との差を表しています。DQNでは、このTD誤差を最小化することで、行動価値関数の推定を改善し、最終的に最適なポリシーに収束させます。

DQNの学習プロセス

  1. 経験リプレイ: DQNでは、エージェントの経験(状態、行動、報酬、次の状態)をメモリに保存し、学習時にランダムなミニバッチを抽出して使用します。これにより、サンプルの相関を減らし、学習を安定させます。
  2. ターゲット$Q$ネットワーク: 学習の安定性をさらに向上させるために、DQNではターゲット$Q$ネットワークを導入します。これは、定期的に更新される主$Q$ネットワークのコピーで、TD誤差の計算に使用されます。

Pongゲームのような環境では、DQNを用いて、画面のピクセルデータから直接最適な行動(例:パドルを上に動かす、下に動かす、動かさない)を学習することが可能です。このプロセスを通じて、エージェントはゲームをプレイする上での効果的な戦略を獲得していきます。


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

Pongと強化学習 (42)

posted by sakurai on July 19, 2024 #837

正解値が無い強化学習でどのように誤差を減らすのかを聞いてみます。


強化学習では、伝統的な教師あり学習のように事前に定義された正解ラベルが存在せず、代わりにエージェントの行動に対する環境からのフィードバックとして報酬が用いられます。このため、強化学習の文脈では、クロスエントロピー損失を直接計算するのではなく、報酬に基づいてエージェントが最適なポリシーを学習する方法が採用されます。

報酬と損失関数

強化学習では、エージェントが取る行動に対して環境から得られる即時報酬(または遅延報酬)を最大化することが目標です。この目標を達成するために、報酬を最大化するポリシーを学習することに焦点を当てますが、報酬の最大化自体を直接の損失関数として使用するわけではありません。代わりに、TD誤差(Temporal Difference Error)や、行動価値関数(Q関数)の更新に基づく損失など、報酬に基づいて間接的に定義される損失関数が用いられます。

行動価値関数とTD誤差

強化学習における一般的なアプローチの一つに、Q学習があります。Q学習では、各状態-行動ペアに対する行動価値関数(Q関数)を推定し、エージェントが最適な行動を選択できるようにします。このとき、TD誤差は現在のQ値と、受け取った報酬及び次の状態における最大Q値との差として計算されます。TD誤差を最小化することで、エージェントは最適なポリシーに従って行動を学習します。

ポリシーグラディエント法

別のアプローチとしてポリシーグラディエント法があります。この方法では、エージェントのポリシー自体を直接最適化します。ポリシーグラディエント法では、報酬の期待値を最大化する方向に、ポリシーのパラメータを更新していきます。このプロセスでは、エピソード全体の報酬の合計を基に損失関数を定義し、この損失を最小化(実際には報酬の最大化)する方向にポリシーを更新します。

結論

従って、強化学習においては、「正解との誤差」を直接的に計量するのではなく、環境からのフィードバック(報酬)を基に最適なポリシーを学習するプロセスが採用されます。TD誤差の最小化、または報酬の期待値の最大化など、報酬に基づいた目標に対して間接的にアプローチすることにより、エージェントは最適な行動戦略を身につけることができます。


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

Pongと強化学習 (41)

posted by sakurai on July 18, 2024 #836

特徴量をどのように行動につなげるのかを聞いてみます。


CNN(Convolutional Neural Network)から得られる特徴量と、それに基づいた行動(例えば、Pongゲームにおけるボールを打ち返す、落とす)の結果に対する報酬の関係を設定するには、強化学習の枠組みが通常使用されます。特に、CNNは環境の状態(ゲーム画面など)から有用な特徴を抽出し、それらの特徴に基づいてエージェントがどのように行動すべきかを学習するために用いられます。

特徴量から行動へのマッピング

CNNを通じて抽出された特徴量は、エージェントのポリシー(状態から行動へのマッピング)の決定に役立ちます。このポリシーは、一般にニューラルネットワークの最後の層(例えば、全結合層)によって実装され、特定の状態においてどの行動が最適か(最も高い報酬をもたらすか)をエージェントが学習することを可能にします。

報酬の設定

強化学習における報酬は、エージェントの行動の結果に基づいて定義されます。Pongゲームの例で言えば、ボールを打ち返したときに正の報酬を、ボールを落としたときに負の報酬(またはより小さい報酬)を設定することで、エージェントはボールを打ち返すことの重要性を学習します。

学習プロセス

  1. 環境の観測: エージェントはゲームの状態(画面の画像など)を観測します。
  2. 特徴抽出: CNNは観測された画像から特徴量を抽出します。
  3. 行動選択: 抽出された特徴量をもとに、ポリシー(ニューラルネットワークの最後の層など)が最適な行動を選択します。
  4. 行動実行と報酬の受領: エージェントは選択された行動を実行し、その結果として報酬を受け取ります。
  5. ポリシーの更新: 受け取った報酬と行動の結果に基づいて、ポリシー(行動選択戦略)を更新します。このプロセスは、エージェントがより良い行動選択を行えるようになるまで繰り返されます。

フィードバックループ

強化学習においては、この学習プロセス全体が継続的なフィードバックループを形成しており、エージェントはトライアルアンドエラーを通じて最適な行動戦略を徐々に学習していきます。CNNから得られる特徴量と報酬の関係を通じて、エージェントはゲームの「ルール」や「戦略」を理解し、パフォーマンスを向上させていきます。


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

Pongと強化学習 (40)

posted by sakurai on July 16, 2024 #834

ReLUのBSVコードを要求してみます。


Bluespec SystemVerilog (BSV) でReLU(Rectified Linear Unit)活性化層を実装するための疑似コードを提供します。ReLUは、入力が正であればその値をそのまま出力し、負であれば0を出力する非常にシンプルな活性化関数です。

ReLU活性化関数の疑似コード

// ReLU活性化関数のインターフェイス定義
interface ReLU_IFC;
    method Bit#(DATA_WIDTH) apply(Bit#(DATA_WIDTH) input);
endinterface

module mkReLU#(parameter DATA_WIDTH = 32)(interface ReLU_IFC);
    typedef Bit#(DATA_WIDTH) DataT;

    // ReLU活性化関数を適用するメソッド
    method DataT apply(DataT input);
        DataT output = (input > 0) ? input : 0;
        return output;
    endmethod
endmodule

説明

この疑似コードは、一般的なReLU活性化関数を実装するBSVモジュールの例を示しています。mkReLUモジュールは、パラメータDATA_WIDTHを使ってデータのビット幅を指定できるようになっています。これにより、異なるビット幅のデータに対応可能です。モジュール内のapplyメソッドは、入力データinputを受け取り、ReLU関数を適用してその結果を返します。具体的には、入力が0より大きい場合はその値をそのまま出力し、そうでない場合(負の場合)は0を出力します。

注意点

  • 上記のコードは疑似コードであり、Bluespec SystemVerilogの正確な文法に従っていない可能性があります。実際にBSVでコーディングする際には、文法や型システムに注意してください。
  • DATA_WIDTHパラメータを使用することで、モジュールの汎用性を高めています。これにより、さまざまなデータ幅に対応することが可能になります。
  • 実際のハードウェア実装では、リソースの使用量や計算速度など、多くの要因を考慮する必要があります。特に、並列処理やパイプライン処理を適切に設計することで、性能を最適化できます。

ReLUはそのシンプルさから、多くのニューラルネットワークモデルで広く使用されています。この疑似コードが、BSVを使用したReLU活性化層の実装に向けた基本的な理解を提供することを願っています。


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

Pongと強化学習 (39)

posted by sakurai on July 15, 2024 #833

Batch NormalizationのBSVコードを要求してみます。


Batch Normalization(バッチ正規化)は、ニューラルネットワークの学習を安定させ、加速するために一般的に使用される技術です。具体的には、ネットワークの各層における入力を正規化して、平均が0、分散が1になるように調整します。これにより、勾配消失や爆発の問題を軽減し、より高い学習率で安定して学習を進めることが可能になります。

ただし、BSV(Bluespec SystemVerilog)で直接的なバッチ正規化層のコードを提供することはできません。しかし、バッチ正規化の基本的なアルゴリズムに基づいて、その処理の流れを説明し、概念的な理解を深めることは可能です。

バッチ正規化の処理の流れ

  1. 平均の計算: 入力データのバッチに対して、特徴ごとに平均を計算します。
  2. 分散の計算: 同様に、特徴ごとに分散を計算します。
  3. 正規化: 各特徴に対して、その平均を引き、分散で割ることで、特徴を正規化します。
  4. スケーリングとシフト: 学習可能なパラメータによって正規化された特徴をスケーリングし、シフトします。これにより、ネットワークが必要に応じて、正規化の効果を打ち消すことが可能になります。

概念的な説明

バッチ正規化層をBSVで実装するには、上記の各ステップをハードウェアで実行可能な形に落とし込む必要があります。特に、平均と分散の計算は、入力データのバッチ全体にわたって行われるため、これらの統計量を計算し、各データポイントに適用するためのメカニズムを設計することが重要です。

疑似コード

Batch Normalization層の操作を疑似コードで示すことは可能です。以下の説明は、Batch Normalizationの基本的な処理を疑似コードで表したものです。この処理には、入力データのバッチに対する平均と分散の計算、それらを用いた正規化、およびスケーリングとシフトのステップが含まれます。

function BatchNormalization(inputBatch, gamma, beta, epsilon):
    # inputBatch: 入力データのバッチ。サイズは[バッチサイズ, 特徴量数]
    # gamma: スケーリングパラメータ。サイズは[特徴量数]
    # beta: シフトパラメータ。サイズは[特徴量数]
    # epsilon: 0除算を避けるための小さな数

    # 平均を計算
    mean = average(inputBatch, axis=0)

    # 分散を計算
    variance = variance(inputBatch, axis=0)

    # 正規化された特徴量を計算
    normalized = (inputBatch - mean) / sqrt(variance + epsilon)

    # スケーリングとシフトを適用
    output = gamma * normalized + beta

    return output
endfunction

この疑似コードでは、バッチ正規化の処理を関数として表しています。この関数は、入力データのバッチ、スケーリングパラメータgamma、シフトパラメータbeta、および0除算を避けるためのepsilonを受け取ります。関数内では、まず入力データのバッチに対して平均と分散を計算し、これらを用いてデータを正規化します。最後に、学習可能なパラメータであるgammabetaを使用して、正規化されたデータをスケーリングし、シフトさせて出力します。

実装上の注意

  • この疑似コードは、プログラミング言語やフレームワークに依存しない一般的な表現です。実際にBSVなどのハードウェア記述言語で実装する際には、配列操作や数学関数(平均、分散、平方根など)をサポートするための追加の設計が必要になります。
  • ハードウェアでの実装では、リアルタイムでの計算速度やリソース使用量など、特有の制約を考慮する必要があります。特に、平均や分散の計算を効率的に行うためのアルゴリズムの最適化や、平方根の計算を近似するための方法などが検討されます。
  • バッチ正規化層は学習プロセス中にパラメータを更新しますが、推論時にはこれらのパラメータは固定されます。そのため、学習と推論をサポートするための設計が必要です。

実装の考慮事項

  • リソース使用量: 平均と分散の計算には、大量のデータに対する加算と乗算が必要になるため、ハードウェアリソースの使用量を考慮する必要があります。
  • 並列処理: BSVの強力な並列処理能力を利用して、平均と分散の計算、および正規化処理を高速化できます。
  • パイプライン処理: データの流れをパイプライン化することで、処理のレイテンシを最小限に抑えることができます。
  • パラメータの更新: スケーリングとシフトに使用されるパラメータは、学習プロセスによって更新される必要があります。これらのパラメータを効率的に更新するためのメカニズムを設計することが重要です。

バッチ正規化層のBSVによる具体的な実装は、これらの基本的な考慮事項に基づいて行われます。実際のコーディングに進む前に、必要な計算の詳細を理解し、適切なハードウェア設計戦略を立てることが重要です。


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


ページ: