質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

1回答

1966閲覧

Batch Normalizationを実装できない

ai_learner

総合スコア10

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2022/02/09 02:18

編集2022/02/11 13:36

![# 解決したい問題
3600個の画像データをミニバッチ処理するCNNを含んだニューラルネットワークの中の活性化関数の手前にBatch Normalizationを入れたいが、一個でも入れると ”ValueError: operands could not be broadcast together with shapes (50176,16) (12544,16) ”と エラーが出てしまい実行できない。
該当する行列はmu(バッチ正規化する際の平均値)の行列

自分なりに考えたこと

バッチサイズが関係していることがバッチサイズだけ変えたときに上記の行列が変化するのでわかった。
データ数をバッチサイズで割り切れないため、最後のバッチのデータ数が足りなくなるのではないかと予想した。
なぜなら、上記の例では、データ数:3600、batch size:64でその商が56と1/4であり、12544は50176の1/4であるからだ。
しかし、バッチサイズをデータ数を割り切れる数字(例:100)に設定したところ、再び、”ValueError: operands could not be broadcast together with shapes (78400,16) (2822400,16) ”とエラーが出てしまい、何が原因なのか、わからなくなってしまいました。

該当コード

テキスト畳み込みニューラルネットワーク(ゼロから作るディープラーニング1のドロップアウトをバッチノーマライゼーションに変更)は文字数制約のため添付写真に表示

バッチノーマライゼーションのコード

Python3

1class BatchNormalization: 2 def __init__(self, gamma, beta, rho=0.9, moving_mean=None, moving_var=None): 3 self.gamma = gamma # 学習によって更新させる. 4 self.beta = beta # 学習によって更新させる 5 self.rho = rho 6 7 # 予測時に使用する平均と分散 8 self.moving_mean = moving_mean # muの移動平均 9 self.moving_var = moving_var # varの移動平均 10 11 # 計算中に算出される値を保持しておく変数群 12 self.batch_size = None 13 self.x_mu = None 14 self.x_std = None 15 self.std = None 16 self.dgamma = None 17 self.dbeta = None 18 19 def forward(self, x, train_flg=True): 20 """ 21 順伝播計算 22 x : CNNの場合は4次元、全結合層の場合は2次元 23 """ 24 if x.ndim == 4: 25 """ 26 画像形式の場合 27 """ 28 N, C, H, W = x.shape 29 x = x.transpose(0, 2, 3, 1) # NHWCに入れ替え 30 x = x.reshape(N*H*W, C) # (N*H*W,C)の2次元配列に変換 31 out = self.__forward(x, train_flg) 32 out = out.reshape(N, H, W, C)# 4次元配列に変換 33 out = out.transpose(0, 3, 1, 2) # 軸をNCHWに入れ替え 34 elif x.ndim == 2: 35 """ 36 画像形式以外の場合 37 """ 38 out = self.__forward(x, train_flg) 39 40 return out 41 42 def __forward(self, x, train_flg, epsilon=1e-8): 43 """ 44 x : 入力. N×Dの行列. Nはバッチサイズ. Dは手前の層のノード数 45 """ 46 if (self.moving_mean is None) or (self.moving_var is None): 47 N, D = x.shape 48 self.moving_mean = np.zeros(D) 49 self.moving_var = np.zeros(D) 50 51 if train_flg: 52 """ 53 学習時 54 """ 55 # 入力xについて、Nの方向に平均値を算出. 56 N, D = x.shape 57 mu = np.mean(x, axis=0) # 要素数D個のベクトル 58 mu = np.broadcast_to(mu, (N, D)) # Nの方向にブロードキャスト 59 print("mu.shape=", mu.shape) 60 61 # 入力xから平均値を引く 62 x_mu = x - mu # N×D行列 63 print("x_mu.shape=", x_mu.shape) 64 65 # 入力xの分散を求める 66 var = np.mean(x_mu**2, axis=0) # 要素数D個のベクトル 67 print("var.shape=", var.shape) 68 69 # 入力xの標準偏差を求める(epsilonを足してから標準偏差を求める) 70 std = np.sqrt(var + epsilon) # 要素数D個のベクトル 71 print("std.shape=", std.shape) 72 73 # 標準偏差の逆数を求める 74 std_inv = 1 / std 75 std_inv = np.broadcast_to(std_inv, (N, D)) # Nの方向にブロードキャスト 76 print("std_inv.shape=", std_inv.shape) 77 78 # 標準化 79 x_std = x_mu * std_inv #N*D行列 80 print("x_std.shape=", x_std.shape) 81 82 # 値を保持しておく 83 self.batch_size = x.shape[0] 84 self.x_mu = x_mu 85 self.x_std = x_std 86 self.std = std 87 self.moving_mean = self.rho * self.moving_mean + (1-self.rho) * mu 88 self.moving_var = self.rho * self.moving_var + (1-self.rho) * var 89 else: 90 """ 91 予測時 92 """ 93 x_mu = x - self.moving_mean # N*D行列 94 x_std = x_mu / np.sqrt(self.moving_var + epsilon) # N*D行列 95 96 # gammaでスケールし、betaでシフトさせる 97 out = self.gamma * x_std + self.beta # N*D行列 98 return out 99 100 def backward(self, dout): 101 """ 102 逆伝播計算 103 dout : CNNの場合は4次元、全結合層の場合は2次元 104 """ 105 if dout.ndim == 4: 106 """ 107 画像形式の場合 108 """ 109 N, C, H, W = dout.shape 110 dout = dout.transpose(0, 2, 3, 1) # NHWCに入れ替え 111 dout = dout.reshape(N*H*W, C) # (N*H*W,C)の2次元配列に変換 112 dx = self.__backward(dout) 113 dx = dx.reshape(N, H, W, C)# 4次元配列に変換 114 dx = dx.transpose(0, 3, 1, 2) # 軸をNCHWに入れ替え 115 elif dout.ndim == 2: 116 """ 117 画像形式以外の場合 118 """ 119 dx = self.__backward(dout) 120 121 return dx 122 123 def __backward(self, dout): 124 """ 125 ここを完成させるには、計算グラフを理解する必要があり、実装にかなり時間がかかる. 126 """ 127 N, D = self.x_.shape 128 129 # betaの勾配 130 dbeta = np.sum(dout, axis=0) 131 132 # gammaの勾配(Nの方向に合計) 133 dgamma = np.sum(self.x_std * dout, axis=0) 134 135 # Xstdの勾配 136 a1 = self.gamma * dout 137 print("a1.shape=", a1.shape) 138 139 # Xmuの勾配(1つ目) 140 a2 = a1 / self.std 141 print("a2.shape=", a2.shape) 142 143 # 標準偏差の逆数の勾配 144 a3 = a1 * self.x_mu 145 print("a3.shape=", a3.shape) 146 a3 = np.sum(a3, axis=0) # Nの方向に合計 147 148 # 標準偏差の勾配 149 a4 = -(a3) / (self.std * self.std) 150 print("a4.shape=", a4.shape) 151 152 # 分散の勾配 153 a5 = 0.5 * a4 / self.std 154 print("a5.shape=", a5.shape) 155 156 # Xmuの2乗の勾配 157 a6 = a5 / self.batch_size 158 a6 = np.broadcast_to(a6, (N, D)) # Nの方向にブロードキャスト 159 print("a6=",a6) 160 print("a6.shape=", a6.shape) 161 162 # Xmuの勾配(2つ目) 163 a7 = 2.0 * self.x_mu * a6 164 print("a7.shape=", a7.shape) 165 166 # muの勾配 167 a8 = -(a2+a7) 168 print("a8.shape=", a8.shape) 169 a8 = np.sum(a8, axis=0) # Nの方向に合計 170 171 # Xの勾配 172 a9 = a8 / self.batch_size 173 a9 = np.broadcast_to(a9, (N, D)) # Nの方向にブロードキャスト 174 dx = a2 + a7 + a9 175 print("a9.shape=", a9.shape) 176 177 self.dgamma = dgamma 178 self.dbeta = dbeta 179 180 return dx

エラー文

~/Downloads/*****/畳み込み/common/layers.py in __forward(self, x, train_flg, epsilon)
371 self.x_std = x_std
372 self.std = std
--> 373 self.moving_mean = self.rho * self.moving_mean + (1-self.rho) * mu
374 self.moving_var = self.rho * self.moving_var + (1-self.rho) * var
375 else:

ValueError: operands could not be broadcast together with shapes (78400,16) (2822400,16) `

ご回答いただけたら、幸いです。](https://ddjkaamml8q8x.cloudfront.net/questions/2022-02-11/a99998cf-725f-4dac-8169-3cab12cdf9b5.png)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ai_learner

2022/02/09 02:25

明記し忘れたのですが、ミニバッチ処理をしております。
ai_learner

2022/02/11 13:37

ご指摘いただきありがとうございます。 質問の方、編集させていただきました。 さらに必要なコードがありましたら、ご指摘いただきたいです。
退会済みユーザー

退会済みユーザー

2022/02/13 06:42

すみません、私も勉強中で、、全部の処理を追い切れていないのですが・・ > 『ゼロから作るDeep Learning』P.188 > ここでは、ミニバッチとして`B={x1,x2,...,xm}`というm個の入力データの集合に対して、平均`μB`、分散`σ^2B`を求めます。 > そして、入力データを平均が0で分散が1になるーー適切な分布になるーーように正規化します。 この式を見ますと、 `mu = np.broadcast_to(mu, (N, D)) # Nの方向にブロードキャスト` のようなところで`N`の数に戻しているところが気になりました。 `__forward`メソッドの最初に`self.moving_mean = np.zeros(D)`してしますので、 `self.moving_mean`と`mu`は`D`の数の配列にしておく必要がありそうに感じました。
ai_learner

2022/02/14 04:26

xg63ex2b様 お忙しい中、目を通していただきありがとうございます。 お恥ずかしながらご指摘いただいた self.moving_meanの配列とmuの配列が異なることを見落としていました。muはxから引く必要があるため、同じ形状(N,D)である必要があります。ですので、self.moving_meanの配列を self.moving_mean = np.zeros_like(x)を使い(N, D)に直しました。 これで、self.moving_meanとmuが同じ配列形状になったので理論上は計算できるはずです。 しかし、改訂後のモデルで実行した結果、同じようなエラー文が出てしまいました。 372 self.x_std = x_std 373 self.std = std --> 374 self.moving_mean = self.rho * self.moving_mean + (1-self.rho) * mu 375 self.moving_var = self.rho * self.moving_var + (1-self.rho) * var 376 else: ValueError: operands could not be broadcast together with shapes (200704,16) (100352,16) 回答いただきありがとうございました。
退会済みユーザー

退会済みユーザー

2022/02/14 06:04

> muはxから引く必要があるため、同じ形状(N,D)である必要があります。 この記載を見て、「ブロードキャスト」についての理解を深めると良さそうに思いました。 > ブロードキャストという用語は、NumPyが算術演算の際に異なる形状の配列をどのように扱うかを説明するものである。ある制約のもとで、小さい方の配列は大きい方の配列に「ブロードキャスト」され、両者の形状が一致するようになります。ブロードキャストは、配列の演算をベクトル化し、PythonではなくC言語でループが発生するようにするための手段です。これはデータの不要なコピーを作成することなく、通常、効率的なアルゴリズムの実装につながります。しかし、ブロードキャストは非効率的なメモリの使用を招き、計算を遅くするため、悪いアイデアである場合もあります。 > https://numpy.org/doc/stable/user/basics.broadcasting.html ある制約のもとで、NumPyが両者の配列の形状を一致してくれるそうです。 次の画像のようなイメージになるそうです。 Broadcastable arrays https://numpy.org/doc/stable/_images/broadcasting_2.png 次のコードでは、全ての行に対して列ごとに減算してみました。 ```py import numpy as np a = np.array([[ 0.0, 0.0, 0.0], [10.0, 10.0, 10.0], [20.0, 20.0, 20.0], [30.0, 30.0, 30.0]]) b = np.array([1.0, 2.0, 3.0]) a - b # array([[-1., -2., -3.], # [ 9., 8., 7.], # [19., 18., 17.], # [29., 28., 27.]]) ```
ai_learner

2022/02/15 05:28

xg63ex2b様 ご返信いただきありがとうございます。 numpyの配列同士の計算は、numpy.broadcast_toで明示的に変更しなくても、片方が同じで、片方が1ならば自動で調整してくれるのですね。スカラーでは経験的に知っていましたが、勉強不足でした。ご指摘いただき、ありがとうございます。最初にご教示いただいた方法で実行してみたのですが、自分のramのメモリ不足で”The kernel appears to have died. It will restart automatically.”と表示され、残念ながら実行できませんでした。以前はなかった現象なので、おそらく計算できたのだと思います。自分でいろいろ対応策を探して、改めて実行したいと思います。ありがとうございました。
ai_learner

2022/02/15 07:04

macを再起動したところ、問題は解決し、ご教示いただいた改訂でBatch Normalizationが動くようになりました。 本当にありがとうございました。
guest

回答1

0

自己解決

修正依頼の欄にあるxg63ex2b様の方法により解決いたしました。

投稿2022/02/15 07:06

ai_learner

総合スコア10

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問