Freenove ESP32-WROVER Board CAMを購入しました。

FreenoveのESP32-WROVERボードは、さまざまな電子プロジェクトや教育目的でよく使用されるマイクロコントローラーボードです。このボードはESP32チップセットを搭載しており、Wi-FiとBluetoothの機能を内蔵しています。Freenoveは通常、そのようなボードとともに詳細なチュートリアルやプロジェクトガイドを提供しているので、プログラミングや電子工学の初心者にも扱いやすいです。

 

アップロードされた画像

画像に写っているのはFreenoveのESP32-WROVERボードとUSBケーブル、そして小型のカメラモジュールです。ESP32-WROVERはWi-FiとBluetoothを内蔵した強力なマイクロコントローラで、さまざまなIoT(モノのインターネット)アプリケーションに適しています。このボードには、外部機器やGPIOピンを接続するための複数のインターフェースがあります。

同梱されているカメラモジュールが示しているように、このキットは画像の取得や処理に関わるアプリケーションに使用することができます。これには、自宅の監視カメラ、QRコードリーダー、AIを組み込んだ物体認識タスクなどの進んでいて興味深いプロジェクトが含まれます。

このボードを使用するには以下の手順を通常踏みます:

  1. USBケーブルを使ってコンピュータに接続します。
  2. Arduino IDEやEspressifのESP-IDFなど、必要なドライバと開発環境をコンピュータにインストールします。
  3. 開発環境を設定して、ESP32-WROVERボードを認識させます。
  4. カメラや他の接続されたセンサーやデバイスを制御するためのファームウェアでボードをプログラミングし始めます。

FreenoveのESP32-WROVERボードとカメラモジュールの使用を開始するための手順を詳しく説明します。

1. ハードウェアの接続

  • ESP32ボードをコンピューターにUSBケーブルで接続します。
  • カメラモジュールをボードのカメラインターフェースに接続します。

2. 開発環境の準備

  • Arduino IDEをダウンロードしてインストールします。(Arduino公式サイトから入手できます。)
  • Arduino IDEを起動し、[ファイル] > [環境設定] で「追加のボードマネージャのURL」にESP32のボードマネージャのURLを追加します。
    • 通常は https://dl.espressif.com/dl/package_esp32_index.json を使用します。
  • [ツール] > [ボード] > [ボードマネージャ]を開き、「ESP32」と検索して、出てきたリストからESP32 by Espressif Systemsをインストールします。

3. ボードとポートの選択

  • ツールメニューからボードを「ESP32 Wrover Module」に設定します。
  • 正しいCOMポートを選択します(Windowsの場合は「COMx」と表示され、Macの場合は「/dev/cu.xxxxx」と表示されます)。どれがESP32ボードに割り当てられているかは、ボードを接続してArduino IDEの[ツール] > [ポート]を見ることで確認できます。

4. プログラムの書き込み

  • サンプルプログラムを開きます。[ファイル] > [サンプル] > [ESP32] > [Camera]などから、カメラを使用するサンプルプログラムを選ぶことができます。
  • サンプルプログラムをボードに合わせて必要に応じて修正します。例えば、Wi-Fi設定やカメラモジュールのピン設定などがあります。
  • プログラムをボードにアップロードします。[スケッチ] > [アップロード]を選択するか、ツールバーの右矢印のアイコンをクリックします。

5. シリアルモニタの使用

  • プログラムによっては、シリアルモニタを通じてボードからの出力を見る必要があるかもしれません。[ツール] > [シリアルモニタ]を開いて確認します。

6. カメラの動作確認

  • サンプルプログラムがカメラの映像をウェブサーバーにストリーミングするものであれば、ボードが提供するIPアドレスにウェブブラウザからアクセスして映像を確認します。

トラブルシューティング

  • もしプログラムのアップロードやボードの動作に問題がある場合は、エラーメッセージを確認してトラブルシューティングの手順を行います。ボードが認識されない、アップロードに失敗するなどの問題が起きた時は、ドライバの問題やUSBケーブルの問題など様々な原因が考えられます。

 

このメッセージは、ESP32へのスケッチのアップロードが成功したことを示しています。ここで表示されているのは、プログラム(スケッチ)がフラッシュメモリに書き込まれ、その後、書き込まれたデータが検証されたというプロセスのログです。

プロセスの要点をまとめると以下の通りです:

  1. esptool.py:これはESP32ボードにスケッチをアップロードするために使われるPythonスクリプトです。
  2. COMポートの確認:シリアルポートが認識され、COM6が使用されています。
  3. ボードの情報の取得ESP32-D0WD-V3 チップの詳細な情報が取得され、この情報にはクリスタルの周波数やMACアドレスも含まれています。
  4. スタブのアップロード:アップロードプロセス中に使用される一時的なプログラムであるスタブがボードにアップロードされています。
  5. ボーレートの変更:アップロードの速度を上げるために、ボーレートがデフォルトから921600に変更されています。
  6. フラッシュサイズの設定:アップロードするプログラムのサイズに基づいてフラッシュメモリのどの部分を消去するかが設定されています。
  7. データのアップロード:様々なセクションへのデータの書き込みが成功し、それぞれのセクションのハッシュが検証されています。
  8. アップロード完了とリセット:全てのデータのアップロードが終了し、ボードはハードリセットを行っています。

このログによれば、アップロードは正常に完了しており、ESP32ボードはリセットされて通常通り動作する準備が整っています。これであなたのスケッチがESP32デバイス上で実行されるはずです。プログラムが意図したとおりに動作しているかを確認するためには、シリアルモニタを開いて出力を確認するか、その他のデバッグ手段を用いる必要があります。

LEDをArduinoに接続する基本的な方法は次のとおりです:

  1. LEDの極性を確認する: LEDにはアノード(長い足、プラス側)とカソード(短い足、マイナス側)があります。
  2. 抵抗を使用する: LEDを直接Arduinoのピンに接続すると、過剰な電流でLEDが壊れる可能性があります。そのため、適切な抵抗(一般的には220Ωから1kΩ)をシリーズに接続して電流を制限します。
  3. 接続:
    • アノード(長い足)をArduinoのデジタル出力ピンに接続します。サンプルコードではピン9を使用していますが、他のPWMが使えるピンでも構いません。
    • カソード(短い足)を抵抗に接続し、抵抗のもう一端をArduinoのGNDピンに接続します。
  4. コードをアップロードする: Arduino IDEから提供されたサンプルコードをアップロードします。
  5. 動作確認: Arduinoに電源を供給すると、LEDがフェードイン(明るくなる)とフェードアウト(暗くなる)を繰り返すはずです。

LEDの極性を間違えてしまうと、LEDは光りません。その場合は、LEDの足を逆にして再度試してみてください。また、使用する抵抗の値はLEDの仕様と使用する電源の電圧によって異なるため、LEDのデータシートを確認するか、オームの法則を使用して計算してください。

 

アップロードされた画像

画像には、Freenove ESP32-WROVER-CAM開発ボードのピンアウト図が示されています。この図は、各GPIOピンの機能を詳細に説明しており、アナログからデジタルへの変換機能(ADC)、デジタル入出力(GPIO)、シリアル通信(I2C, SPI)、PWM出力などの機能を持つピンを識別するのに役立ちます。

もしGPIO0(表記はIO0ともされます)が利用できない場合、このピンアウト図に基づいて他のピンを選択できます。アナログ入力が必要な場合は、ADC1_CH0からADC1_CH7またはADC2_CH0からADC2_CH9までのいずれかのピンを使用できます。ただし、ESP32ではADC2チャンネルがWi-Fi機能と共有されているため、Wi-Fiを使用しているときはADC2チャンネルをアナログ読み取りに使用することはできません。

もしGPIO0の代わりに別のデジタルピンが必要な場合は、ピンアウト図からGPIO1からGPIO39までの範囲のいずれかのピンを選ぶことができます(ただし、ピンの中には入力専用や特定の機能に予約されているものもあります)。

 

次回はカメラを試してみます。

ANOGAN 第一話

異常検知システムを作ってみたい

しばらく深層強化学習の分野に力を入れていましたが、次は生成系ニューラルネットワークを使って異常検知にチャレンジしてみます。

異常検知システム

異常検知の種類を分類すると

  • 外れ値検知:正常データの分布から外れているデータを検知
  • 変化点検知:時系列データの傾向が変化した点を検知
  • 異常部位検知:時系列データに対して正常パターンから外れているパターンを検知。

に分けられます。

異常検知の難しい点

とあるアルゴリズムで異常を検知したが、それが本当に異常なのか判断するのは、ドメイン知識がなければできません。

AutoEncoder

ニューラルネットワークを使った異常検知として基本的なアルゴリズムにAutoEndocerがあります。

正常データを入力しエンコーダーで圧縮し、再びデコーダーで元データに戻します。その過程で特徴が抽出されます。

出力の教師データに入力と同じ正解データを使うことによって徐々に各ネットワークパラメータを学習していきます。

学習後、データを入力したときに出力がデータと大きな差があれば、それは学習できていない未知のデータであり正解データとは異なる、つまり異常データと判断できます。

 

GAN

生成系と言ったらGANが有名ですが、異常検知システムに使われるANO-GAN(Anomaly Generative Adversarial Networks)を使って設備の波形データ異常を検出できるかやってみましょう。

まず、anoganを使用するためには、PythonのディープラーニングフレームワークであるPytorch, TensorFlowやKerasが必要です。これらのライブラリをインストールしてセットアップする必要があります。また、適切なデータセットも必要です。

 

大まかな手順

  1. ライブラリのインストール
  2. データセットの準備
  3. Generator(生成器)の定義
  4. Discriminator(識別器)の定義
  5. モデルのトレーニング
  6. 異常スコアの計算
  7. 異常の検出と評価
  8. ハイパーパラメータのチューニング

 

 

  1. ライブラリのインストール: Pytorch, TensorFlowやKerasをインストールします。私はPytorchを使ってます。
  2. データセットの準備: 正常データだけあればモデルを作れるのがANOGANのメリットですが、異常が含まれるデータも検証で使いますので、結局は正常、異常の両方のデータを準備する必要があります。
  3. Generator(生成器)とDiscriminator(識別器)の定義: anoganは、生成器と識別器の2つのネットワークで構成されます。生成器は、正常なデータを生成し、識別器は正常なデータと異常なデータを識別します。
  4. モデルのトレーニング: 正常なデータセットを使用してモデルをトレーニングします。トレーニング中に生成器と識別器を交互に更新し、互いに競い合わせることで、正常なデータの特徴を学習します。
  5. 異常スコアの計算: モデルがトレーニングされたら、生成器を使用して異常スコアを計算します。異常スコアは、生成器が正常なデータをどれだけ正確に再現できるかを示す指標です。異常スコアが高いほど、異常と判断される可能性が高くなります。
  1. 異常の検出と評価: 異常スコアを閾値と比較し、異常と判断されるデータを検出します。また、モデルの性能を評価するために、テストデータセットを使用して評価指標(精度、再現率、F値など)を計算します。
  2. ハイパーパラメータのチューニング: モデルのパフォーマンスを改善するために、ハイパーパラメータのチューニングを行います。モデルのアーキテクチャ、学習率、バッチサイズなどのパラメータを調整し、最適な結果を得るようにします。

 

以上が基本的な手順ですが、anoganを使用した異常検知システムの構築は一般的には複雑なタスクです。データセットの準備やモデルのトレーニングには時間と計算リソースが必要であり、さまざまな試行錯誤が必要となる場合もあります。

 

ではやっていこう。

 

DDPG by gymnasium 17日目

今回は、単純にノードを64から32に落としてみたらどうかと思います。

学習のステップ

  1. 100Steps x 1000Epsodes
  2. 200Steps x 1000Epsodes
  3. 300Steps x 1000Epsodes

非常にうまくいきました。文句なしです。

走りも安定しています。

 

これで、このDDPGシリーズはお終いにいたします。

続編はMalti Agent Deep Detarministic Policy Gradient:MADDPG の予定です。ありがとうございました。

 

 

DDPG by gymnasium 16日目

前回

学習が進まず、チーターの様子を見ると開始直後に静止してお終いです。ぐーたらになってしまいました。

考えうる原因

  • ノード数が多すぎる?256
  • 学習率が大きすぎる?0.01
  • パラメータの初期値 Heがうまくいってない?/1√256は小さすぎる?
  • レイヤー正規化が上手くいってない?

→レイヤー正規化をキャンセルしてみます。

actor:
def forward(self, obs):
        #print(‘AgetDDPG.ActorNN.forward is working’)
        #print(‘====ここまではOK1====’)
        x = self.fc1(obs)
        #x = self.bn1(x)
        x = F.relu(x)
        x = self.fc2(x)
        #x = self.bn2(x)
        x = F.relu(x)
        x = self.fc3(x)
        action = F.tanh(x)
        return action

 

critic:

def forward(self, obs, action):
        input_data = T.cat([obs, action], dim=1)
        x = self.fc1(input_data)
        #x = self.bn1(x)
        x = F.relu(x)
        x = self.fc2(x)
        #x = self.bn2(x)
        x = F.relu(x)
        x = self.fc3(x)
        return x #一つの状態価値を出力する。
結果:ダメです!学習が進みません。
  
次はHeの初期値をキャンセルしてみます。
→結果:ダメです、収益が徐々に下がり気味で、Actor_lossもなぜか安定して増えていっています。
次はノードを256から64に落としてみます。また同時にバッチサイズも256から64に落としてみます。
→結果:Stepは200にして始めています。
300回やったあと、続けて1000回やってみました。頑張ってる?
ここでStepを300にしてみます。安定して前進している姿を経験再生バッファに溜め込むのが目的です。
悪化しました・・・。
ということは、残すところ学習率?0.01から0.001に細かくしました。
100Stepを1000Episode、200Stepを1000Episode実行しました。
上手くいきました。

ActorNN:He適用しない, alpha=0.0001, Adam, bn1,キャンセル bn2キャンセル

batch64,obs17→17 fc1 64→64 ReLU 64→64 fc2 64→64ReLU64→64fc3 6 →Tanh6→ action6

CriticNN:He適用, beta=0.001, Adam,bn1,キャンセル bn2キャンセル

batch64,obs17+act6→input23  fc1 64→64 ReLU 64→64 fc2 64→64fc3 1→活性化関数なし→Q_value 1

結果的に、

  • ノード数が多すぎる?256→64
  • 学習率が大きすぎる?0.01→0.001
  • パラメータのHe初期値 →キャンセル
  • レイヤー正規化→キャンセル

でうまくいきましたが、学習率が大きすぎたのが原因ではないかと考えます。

またノード数が多すぎて過学習を起こしていた可能性もあります。

またレイヤー正規化が上手くいかないのも腑に落ちません。

He初期化は原因として考えにくいので、まずはここだけ戻してみます。

問題ありませんでした。次にレイヤー正規化を適用してみます。

600Epsodeを過ぎたあたりで悪化していますが、何とか持ち直しています。どうやら、スタートダッシュで前傾姿勢になりすぎて逆に速度が遅くなっているようです。ひっくり返っているのでしょう。1000回超えたあたりでStepを伸ばしたので前のめりが修正されつつあると考えます。よってStep300にして1000回続けてみます。

ブレ幅が大きいですが、よさそうです。

いや、やっぱダメです(笑)ずっと逆立ちして頭と前足で前進しています。

600回~900回あたりで、変な癖を覚えてしまったようです。

ほんとAIってやつは突拍子もないことを考え付きます。

原因は何でしょうか、過学習と言っていいものかはさだかではありませんが、正則化のようにニューラルネットワークの最適化に対して何らかの拘束を与えるとよいかもしれません。正則化というとよく活性化関数からの出力のうち10%程度を0にして学習をさせる、ドロップアウトがでてきますが、単純にノードを64から32に落としてみたらどうかと思います。次回やってみます。

 

DDPG by gymnasium 15日目

さて最終章に近づいてまいりました。(ような気がする)

ニューラルネットワークの学習をより安定化させるために標準化Standarzation,正規化Normalizationをやっていきましょう。

まずは入力値について考えます

各特徴量ごとの学習の感度を同じくらいにそろえるために、入力値の値を相対的に加工します。

標準化

Z-scor normalization

手持ちデータを平均ゼロ、分散1の正規分布にする手法

正規化

Min-Max normalization

最小値~最大値を0~1にスケーリングする手法

 

使い分け

標準化:最大最小が決まっていない場合、外れ値が存在する場合

正規化:最大値および最小値が決まっている場合

戦闘力は標準化が良い

住宅価格も標準化が良い

弱点

弱点として、入力値はいいのですが、ネットワークを伝播していくうちに各ノードの入力値(活性化関数がかかる前)の分布は崩れていきます。

すると、勾配消失が起こりやすくなります。

重みパラメータの初期値を工夫する

そこで重みの初期値を工夫して一様分布や正規分布に従うランスを使うのですがディープラーニングではうまく動かないことがあります。

解決策として 乱数xネットワークの大きさに合わせた係数 を初期値とすると 分布が崩れにくくなるそうです。(ちょっとわかりません。)

活性化関数がシグモイド関数の場合はXavierの初期値、ReLU関数の場合はHeの初期値というものが良いそうです。

重みの要素数がnである場合、Heの初期値は1/√nとなります。

 

バッチ正規化

もっと直接的で良い方法があります。

各ノードに入力されたら、そこで正規化してしまえば良いのです。各層で毎回正規化を繰り返します。

因みになぜバッチ正規化というのかはわかりません。各レイヤーのノード群をバッチと呼んでいるのでしょうか。

とにかく各層のノードを正規化するのです。

学習の成功率を上げるとともに過学習もしにくくなるらしいです。

やりかた

標準化して線形変換するだけです。

標準化:

あるレイヤーのノード群の平均値μと分散sを求める

各ノードの値xから平均値μを引いて標準偏差σで割った値が標準化された値z

線形変換:

zにパラメータw,バイアスbを使って y=wz+bへ線形変換する。

正確に言うとこれはバッチ正規化の発展系であるレイヤーノーマライゼーションを使っています。

ActorNNクラスの改造:

重みパラメータ、バイアスパラメータの初期値

今まではランダムに初期値を決めていたところをHeの初期値に変更しました。つまり、ネットワークの各層のノード数ActorNN.各fc.weight.data.size()[0]に応じて、初期値の上下限f1,f2を決定するようにしました。nn.init.uniform_()。

各層の入力値を正規化

今までは各層への入力値は特に加工せずに活性化関数へ入れていましたが、入力値は正規化nn.LayerNorm()を適用してから活性化関数に入れるように変更しました。入力値→正規化bn→活性化関数ReLU。

CriticNNクラスの改造:

こちらも考え方は同じです。

学習結果

1000エピソード以上学習しましたが、どういうことでしょう調子いいのは300エピソードくらいのところで、そこから一気に悪化しています。

横軸:エピソード、縦軸:収益

ハーフチーターは一体何をやっているのでしょうか?

開始直後に素早く美しく前転し、ひっくり返りながらバタバタしていました。どうやら300回転超えたあたりから学習するべきデータが間違ったものしかない状況になり、回復しなかったと推測します。負のスパイラルに落ち込んでいったのでしょう。

反省

今回の条件を確認します。

agentインスタンス:

alpha=0.01,
beta=0.01,
gamma=0.99,
tau=0.01,
n_obs_space=17 ,
n_action_space=6,
n_state_action_value=1,
layer1_size=256,
layer2_size=256,
layer3_size=256,
batch_size=256,

AgentDDPGクラス__init__():

alpha=0.000025,
beta=0.00025,
amma=0.99,
tau=0.001,
n_obs_space=17 ,
n_action_space=6,
n_state_action_value=1,
layer1_size=64,
layer2_size=64,
layer3_size=64,
batch_size=64,
self.actorインスタンス:
alpha=0.000025,※ここ間違った。alpha=self.alphaにするべき
n_obs_space=17,※ここ間違った。同上
n_action_space=6,※ここ間違った。同上
 layer1_size=64,※ここ間違った。同上
layer2_size=64,※ここ間違った。同上
layer3_size=64,※ここ間違った。同上
batch_size=64※ここ間違った。同上

なので actorとしては

ActorNN:He適用, alpha=0.000025, Adam

batch64,obs17→17 fc1 64→64 bn1 64→64 ReLU 64→64 fc2 64→64 bn2 64→64ReLU64→64fc3 6 →Tanh6→ action6

※layer3_sizeは不使用

という状況でした。一部間違っていました。直します。

修整後↓

self.actor = ActorNN(device=self.device, alpha=self.alpha, n_obs_space=self.n_obs_space, n_action_space=self.n_action_space,
                            layer1_size=self.layer1_size, layer2_size=self.layer2_size, layer3_size=self.layer3_size, batch_size=self.batch_size)

ちなみにActorNNの初期化メソドは

ActorNNクラス__init__():
alpha=0.001
n_obs_space=17,
n_action_space=6
layer1_size=256,
layer2_size=256,
layer3_size=256,
batch_size=64

です。

target_actorインスタンスも下記のように間違っているので

self.target_actor = ActorNN(device=self.device, alpha=0.000025, n_obs_space=17, n_action_space=6,
                                    layer1_size=64, layer2_size=64, layer3_size=64, batch_size=64)

直します。↓修整後

self.target_actor = ActorNN(device=self.device, alpha=self.alpha, n_obs_space=self.n_obs_space, n_action_space=self.n_action_space,
                                    layer1_size=self.layer1_size, layer2_size=self.layer2_size, layer3_size=self.layer3_size, batch_size=self.batch_size)

 

次に、target_criticインスタンスを見ていきます。

beta=0.000025,※ここも間違い。self.betaにするべき
n_obs_space=17,※同上
n_action_space=6,※同上
 layer1_size=64,※同上
layer2_size=64,※同上
layer3_size=64,※同上
batch_size=64)※同上
これも間違っているので修正。修整後はこちら↓
self.target_critic = CriticNN(device=self.device, beta=self.beta, n_obs_space=self.n_obs_space, n_action_space=self.n_action_space,
                                    layer1_size=self.layer1_size, layer2_size=self.layer2_size, layer3_size=self.layer3_size, batch_size=self.batch_size)

同じくtarget_criticインスタンスもself.なんちゃらにしていないので修正が必要です。修整後↓

self.critic = CriticNN(device=self.device, beta=self.beta, n_obs_space=self.n_obs_space, n_action_space=self.n_action_space,
                                    layer1_size=self.layer1_size, layer2_size=self.layer2_size, layer3_size=self.layer3_size, batch_size=self.batch_size)

なので target_criticとしては

CriticNN:He適用, beta=0.000025, Adam

batch64,obs17+act6→input23  fc1 64→64 bn1 64→64 ReLU 64→64 fc2 64→64 bn2 64→64fc3 1→活性化関数なし→Q_value 1

※layer3_sizeは不使用

ちなみに
CriticNNクラスの__init__()
beta=0.001,
n_obs_space=17,
n_action_space=6,
 layer1_size=256,
layer2_size=256,
layer3_size=256,
batch_size=64):
です。

現在までのまとめ

学習率とノード数を間違って入れていましたが、これが悪いとは現時点では言えません。

可能性としてはバッファメモリが10000で1エピソード2000ステップなので5回連続でダメな収益結果の場合、「ひっくり返ってお終い」、のようなダメな経験しか持ってない状態になります。

これが、二度とまともな状態に学習が継続できない要因ではないでしょうか。

(メモ:ステップを400とすると1エピソードがなぜか5倍の2000ステップまで実行されます。仕様ですかね。)

修正したコード

ひとまず、間違っていた部分を直したので張っておきます。

agent = AgentDDPG(device=device, alpha=0.01, beta=0.01, gamma=0.99, tau=0.01,
                  n_obs_space=17 , n_action_space=6, n_state_action_value=1,
                  layer1_size=256, layer2_size=256, layer3_size=256, batch_size=256, mode=EVAL_TRAIN_MODE) # cuda追加
がネットワーク生成までこのパラメータが適用されるように修正しました。
これの結果:学習が進みません。チーターの様子を見ると開始直後に静止してお終いです。ぐーたらになってしまいました。

ノード数が多すぎる?256

学習率が小さすぎる?0.01

パラメータの初期値 Heがうまくいってない?/1√256は小さすぎる?

レイヤー正規化が上手くいってない?

次回へ続きます。

DDPG by gymnasium 14日目

本日のお題は

保存したパラメータを読み出すのはactorとtarget_actorまたcriticとtarget_criticで共通で良いのだろうか。

です。結果として共通で良いです。

つまりactorとtarget_actorはactor_params.ptを呼び出して、criticとtarget_criticはcritic_params.ptを呼び出せば成り立ちます。

ただ、実行開始直後が同じパラメータになってしまうので、リプレイバッファに経験が貯まりTDターゲットの再計算が始まるまではcriticとの差が小さいので、しばらくは学習の進み方が遅くなるのではないかと予想しております。

実際は問題になるほどではありませんでしたが、ここはセオリー通り共通ではなくてそれぞれのパラメータを読み込むように修正しました。

ActorNNクラスの__initi__()の中に

そしてCriticNNクラスの_init__()の中に

と書いていたのを削除して、代わりに

actor, ciritc, target_critic, target_actorそれぞれのインスタンス生成直後に入れています。