2026.03.05 18:13
CNNのバックプロパゲーションが見えてきた&全結合の修正で認識率96%
・というところで、バックプロパゲーションを・・・と思って書き出そうとしたらいきなりVSCode君がコードを吐き出してくれた。たぶんCoPilotが生成したんだろう。
・なんとなく面白いけどねと思いながら、それを下に置いて1行ずつ自分で改めて書きながら読むということをしていたのだけど、途中でわけがわからなくなってきた。
・ということで、困った時のいつものGCC・・・この手のはGeminiも得意だよねということで質問していたら、なんか話が噛み合わない。
・で、ある程度まとまったコードブロックを提示したら、「それ、変だよ」と指摘してきた。
・いちいち指摘が的確というのか、「なんでこの値を掛けてるんだ?」と疑問に思っていた部分がやはり間違っている。
・「CoPilotが自動生成したんだけどね」といったら「あはは・・そうでしたか」ときて、CoPilotに対してちょっと「あいつのコード生成はね・・・」的な指摘をしてくるのがまた可笑しい。
・というところで、じっと考えて整理できてきた。わかってしまえばそれほど難しくは無いのだけど、バッチサイズ分まとめてたり、フィルタ数分まとめて考えていたりというのを一度に考えると頭の中で行列がごちゃごちゃしてややこしいのだな。
・そんなこんなでやっていたら、全結合の方で軽い勘違いを見つけた。まぁ、2層程度なら問題ないんだけど、バックプロパゲーションで前段に戻すときにもLR(Learning Rate)を掛け算してしまっていたという凡ミス。
・これを修正してLRを更に小さくして(でないと、途中でオーバーフローしやすい)再度全結合でMNISTしてみたら正答率96%となった。うん、標準的な値とされる範囲になったな。
2026.03.04 17:46
とりあえず順方向は良いかな
・ぼちぼちとpythonで書く。四次元配列に混乱しそうになりながらとりあえずCNNクラスの__init__()と順方向はできたかな?
・バックプロパゲーションがちょっと難しい。というのか、4次元の配列を眺めているとどうも頭が混乱しそうになる。
・もう一回ちょっと整理しよう
2026.03.03 23:41
CNNの仕組みがだいぶ見えた・・・かな
・さて、GCC(Gemini/Chatwork/Copilot)君に書かせたコードを眺めていると、フィルタの枚数やX,Yのサイズなどの他にChannelというパラメータがある。何だこれ?ということでお尋ねしたら例えばカラー画像データのRGBのようなものとのこと。なるほどね。
・たとえばRGBであればそれぞれについてフィルタをかけてやって、RGBの三枚分のデータを全部足し算するという流れらしい。
・PyTorchなどのライブラリを使えば楽できるらしいけど、今回はNumPyだけでやるので地道にループさせる。
・で、もう一つ。出力側にもやはりチャンネルがある。画素ごとの重みをつけるフィルタの構造は
filter[出力チャンネル数、入力チャンネル数、Yサイズ、Xサイズ]
という4次元配列になる。たとえば、入力がRGBの3チャンネルあって、フィルタのサイズが5x5ならば
filter[出力チャンネル数,3,5,5]
つまり、入力チャンネル数×出力チャンネル数分のフィルタがあるということ。これはつまり、入力画像の1つのチャンネルの一つずつ(たとえばRGBならばRの画像など)についてN個のチャンネル(つまりN個のフィルタ)があるという具合。
・要するに演算の考えとしては1つのニューロンが元画像の3x3なり5x5なりといった狭い領域について全結合ニューラルネットと同じような積和演算をしていて、これがズラッと並んでいる(元画像の1ドットずつシフトしながら)。
・外から見るとちょうど元画像を底面としたピラミッドがたくさん重なり合いながら並んでいる感じで、これが入力チャンネル数×出力チャンネル数個あるわけだ。
・全結合のように、このニューロンすべてが独立したウェイトとバイアスをもたせるという手もあるけど、そこを節約して同じ出力チャンネルに属するニューロンのウェイトやバイアスは共通で使うことにした。それをフィルタと称している・・・とまぁそんな感じで解釈できるのだろう。
そんなことをすると要求されるメモリ量が膨大なものになってしまう。そこで同じチャンネルのニューロン(要するに一つのピラミッド群)
のウェイトやバイアスは同じものを使うことにしていると思えば良いのだろう。
・
3x3とか5x5とかのパターンを使いまわしているという点
全結合ニューラルネットの1層目なんかと同じようなものと思えば良いのかな?と
イメージ的には全結合のときに同じ入力に対してズラッとニューロンが並ぶようなのと似ていて、同じフィルタをかけたものを
・
2026.03.02 08:33
Claudeか。生成AIの名前はCがお好き?
・米国国防省がらみで花札君がお怒りになったとかいうClaude(日本語版はこちら)をちょっとお試し。
・アカウントを作らなくても、このAIチャットのところで遊べるのでちょっと試してみたけど、なんとなくしっくりこない。Geminiのおべんちゃら(死語?)にもいささか参るけど、こちらの回答の雰囲気もなんとなく。
・ちょっとしたプログラムを作らせてみたらまぁ普通に生成してくれたし、読みやすくなってはいるけれど、もう少しちゃんとコメント入れてほしいかなという感じ。
・これもお仲間に入れるとGCCCか。生成AIの名称は’C’がお好き?
2026.03.01 07:37
CNNのためのテストパターンを作っておこう
・さて、CNNの仕組みとしてはほぼ正解だった(らしい)ので、現状の全結合ニューラルネットの前処理的に畳み込み層とプーリング層を追加することに。
・いきなりMNISTで試すとわけがわからなくなりそうなので、単純なものでフィルタがどう形成されるのかわかるように8x8の単純なパターンを作成してまずは全結合でテスト。
・最初うっかりして全く同じデータに違う答えを与えたら見事に収束せず。あわてて修正したらすぐ収束して予定通り。
・さて、ぼちぼち書いていこう。
2026.02.28 23:58
CNNってこういう感じで良いのかな?
・CNNのバックプロパゲーションを考える。
・要するにフィルタを移動させて得た画像データを縮小したものに対する補正値が戻ってくるわけだ。
・たとえばフィルタを3x3として、2x2の4ドット分を1ドットに縮小して、このドットのデータが全結合ニューラルネットに入るという構成を考える。
・一応、今の理解では順方向の操作は
1)元画像の4x4の四角い領域を3x3の除き窓で覗く。
2)左上、右上、左下、右下の4箇所の3x3の領域について、それぞれの画素データと、対応する位置のフィルタのデータを掛け算して和を取る。(イメージとしては、照度センサの前にパターンの描かれたシートを貼り付けもので明るさを測っているようもの)
たとえば
フィルタ側を
F0 F1 F2
F3 F4 F5
F6 F7 F8
というデータ、画素側が
G00 G01 G02 G03
G04 G05 G06 G07
G08 G09 G10 G11
G12 G13 G14 G15
として、フィルタが左上なら
F0xG00 F1xG01 F2xG02
F3xG04 F4xG05 F5xG06
F6xG08 F7xG09 F8xG10
となるわけで、これら9つを足したものに更にバイアスを足したものをつくる(仮にSUM00とかする)
3)窓を元画像全体に渡って1ドットずつ(1枠ずつではなく)移動させると
SUM00 SUM01 SUM02 SUM03・・・
SUM10 SUM11 SUM12 SUM13・・・
SUM20 SUM21 SUM22 SUM23・・・
・・・・
となる
4)こうして得られたものをたとえば縦横2ドットずつまとめるなら
SUM00 SUM01 SUM02 SUM03
SUM10 SUM11 SUM12 SUM13
といった4画素分ずつまとめて、たとえばこの中の最大値を採用して4ドットを1ドットにする。これがプーリング層と呼ばれるものの役割ということらしい。
5)全フィルタについて同じようなことをする。ここまでの例なら
元画像の画素数÷4×フィルタの枚数
個のデータ列になる。これを全部まとめて全結合ニューラルネットに食わせる。
という具合。
・さて、このときたとえば、左上の4つをまとめたものに対してバックプロパゲーションで戻ってきた値というのは、SUM00,SUM01,SUM10,SUM11の4つに共通して使われる。
・ただ、「誤差の責任」はプーリングした時に採用したもの・・・たとえばSUM00だけとか、SUM11だけとか・・・にしかないので、バックプロパゲーションで調整するのにつかうのは「採用したもの」だけ
・たとえばSUM00が採用されたものだとすると、これは
F0xG00 F1xG01 F2xG02
F3xG04 F4xG05 F5xG06
F6xG08 F7xG09 F8xG10
の和だったわけで、とすると「誤差の責任の割当」は全結合のバックプロパゲーションと同じ。全結合層から戻された値をVback、学習率をLRとすれば、たとえばF00の補正はF00 ー= Vback*LR*G00てな具合。
・これをプーリング層出力の(元画像画素数÷4)個分処理するとフィルタ1個分が補正される。で、全フィルタ数分やれば全部のフィルタが補正される。以下は繰り返しである。
・ただ、加算されていく数が多いので発散しかねないから、全部足した後でプーリング層出力の数で割って平均を取ると良いんだろうな。
・たぶん、こんな感じで大きく外してはいないと思うのだけど。もう一回本当にそうなのか確認しておこう。
2026.02.27 16:53
CNNを試すか。ちょっと予備学習
・まぁそんなところで、全結合ニューラルネットワークの方はほぼ作り方がわかった(気がする)。活性化関数を色々変わったものに変えたり、ウェイトやバイアスの変化のさせ方を弄ったりしても面白いことが起きるのだろうけど。
・というところで、mnistで遊んでいたためか、GCC(Gemini/Chatwork/Copilot)君からは「CNNやってみない?」というお題がきた。
・全結合だと画像が上下左右に動いたりしたときに全く違うデータとして扱われてしまうので、これをまとめてしまおうという仕掛けがCNNということらしい。
・CNNはConvolutional Neural Networkの頭文字をとったもので一般的には「畳み込みニューラルネットワーク」と訳されているけど、Convolutionalのvolutionって回転を意味してなかったっけ?と辞書を引いたらほら案の定。
・「くるくる撒いた状態、渦巻き、込み入った状態、複雑」っていう感じで、なんとなく糸巻き的なものをイメージするようなネーミングではある。
・やっていることをちょっと検索してみた今現在の理解はこんな感じ
1)に小さい四角い窓(2X2とか3X3とか)を用意して、この中で重み付けをする(フィルタと呼んでいる)。すると、例えば横線と右斜め下がりの線とかの特徴があるとみなせる部分が強く現れる。(真っ白でも反応するけど、まぁそれはそれとして)
2)この窓をスライドさせて新しい行列を作る。
3)そして、このフィルタのパターンを色々用意しておいてそれぞれごとに行列を作る
4)フィルタが画素外に出てしまわないようにすると、その分行列がちいさいなるのでその領域は0などでパディングしておく。
・ここまでが「畳み込み層」とよばれるもので、この後が「プーリング層」とよばれているようだ。
5)こうしてできたものをたとえば縦横1/2に圧縮する。まぁ1/2なら縦横2つの画素の中の最大値をとるという方法が紹介されていたけど、「ぼかしている」という感じでもある。すると、縦横1ドットずれても結果は同じになる。
6)こうして出来上がったもの(縦横1/2の画像データ×フィルタ数)分のデータを全結合ニューラルネットで学習させる。
・そうすると、たとえば、横棒に強く反応した画素の右隣に縦棒に強く反応する画素があったとすればこれはそのあたりの場所で十字とかT型などの交差に反応するようなニューロンが生まれる可能性があるということになるのだろう。
・VOUTIONの字句を活かすなら、製糸工場など糸を紡ぐときのように、「窓」エリアから伸びた細い糸(画素データ)がまとめられていくようなのをイメージすると良いのかなという感じ。
・前処理はたぶんちょっとデータ数も多いけど「難しくはないけど手間はかかる」という類だろう。
・たぶん、フィルタ部分も初期値は適当に決めて学習するに従って最適化していくことになるのだと思うのだけど、この学習のやり方がCNNのツボなんだろうな。
2026.02.26 09:22
全結合2層がmnistで約95%。それなりに認識できた…かな?
・さて、MNISTでいろいろやってみている。先頭の1000個を学習させて同じデータで判定を見るとほぼ100%(正答数が1000個だったり999個だったり)となる。
・ならばということで次の1000個の判定をやらせたら87%程度に落ちてしまった。
・過学習なのかなとパラメータ類をいじってみたり、入力データをシャッフルして順番がバラバラになるようにしてみたけどあまり変わらない。
・ということは学習するパターンが少なすぎるのかなということで、6万個のデータのうち5万個を学習用に使って(1000個ずつ50回に分けて毎回シャッフルしてから学習)これを100回繰り返し学習。その後5000個目からの1000個でテストしたら正答率94.8%と出た。
・まぁ、いまどきmnistでの正答率は99.5%以上が当たり前な世界らしいから誤答率が1桁違うけど、何の工夫もない単純な2層のネットワークでもこのくらいまで追い込めるんだな。
・一般的にはどうなんだろうと思ったけどやった人は少なくてPyTorchを使った例などを見ていても3層にしていて、第1層が画素データ数あることにしているらしい。
・こちらは第1層が128個、出力層が10個ということを考えるとこんなもんか。
2026.02.25 16:24
MNIST面白いな
・ご近所イオンの三井銀行が混んでるなと思ったら今日は25日だったか。
・なんとなく「グノーシア」を見ているけど、なんとなく登場するキャラクタとその色付けだけ決めて、どういう話が作れるかという感じだな。まぁ、Re:ゼロに見られたようなループものの極限というところか。
・というところで、MNISTを扱ってみる
といっても、対して難しいことはなくて、28x28の784バイト分の入力があり、0から9に対応するワンホット出力(正解のところだけ1になる出力)が10個。間は適当で128個の[784,128,10]の2レイヤのニューラルネット。MNISTデータの読み込みはこれまたGCCに尋ねてこんな感じでいけるなというところ
def load_mnist_images(filename):
with open(filename, 'rb') as f:
# 最初の16バイト(ヘッダー情報)を飛ばす
data = np.fromfile(f, dtype=np.uint8, offset=16)
# 28x28の画像に成形し、0.0〜1.0に正規化
return data.reshape(-1, 784) / 255.0
def load_mnist_labels(filename):
with open(filename, 'rb') as f:
# 最初の8バイト(ヘッダー情報)を飛ばす
data = np.fromfile(f, dtype=np.uint8, offset=8)
return data
で、まぁ、こんな感じにすればXに学習データ、Tに答えが入る計算。
X_train = load_mnist_images('../mnist/train-images-idx3-ubyte')
Y_train = load_mnist_labels('../mnist/train-labels-idx1-ubyte')
X_train.reshape(-1,784)
X = np.array(X_train[:1000])
indices = np.array(Y_train[:1000])
size = 10
T = np.eye(size, dtype=int)[indices]
とりあえず6万個は多すぎるので、1000個くらいで試すことにしてどうなるかなとやってみたら、いきなりオーバーフロー連発。ウェイトの初期値にsqrt(2.0/size)を掛けると良いよとか、学習率は0.01くらいにしてみては?というアドバイスをGCCにいただきながらちょこまかと自分なりに調整していったら結構いい感じで収束しはじめた。
だいたい800回くらい回すとほぼ落ちついてきて、学習後の先頭100個の判定結果と教師データ(正解)はこんな感じ
出力結果:
[5 0 4 1 9 2 1 3 1 4 3 5 3 6 1 7 2 8 6 9 4 0 9 1 1 2 4 3 2 7 3 8 6 9 0 5 6 0 7 6 1 8 7 9 3 9 8 5 9 3 3 0 7 4 9 8 0 9 4 1 4 4 5 0 4 5 6 1 0 0 1 7 1 6 3 0 2 1 1 7 9 0 2 6 7 8 3 9 0 4 6 7 4 5 8 0 7 8 3 1]
教師データ:
[5 0 4 1 9 2 1 3 1 4 3 5 3 6 1 7 2 8 6 9 4 0 9 1 1 2 4 3 2 7 3 8 6 9 0 5 6 0 7 6 1 8 7 9 3 9 8 5 9 3 3 0 7 4 9 8 0 9 4 1 4 4 6 0 4 5 6 1 0 0 1 7 1 6 3 0 2 1 1 7 9 0 2 6 7 8 3 9 0 4 6 7 4 6 8 0 7 8 3 1]
・2層にしただけでこんなにうまく行くものなのか。なんだか面白いな。
・もう少しMNISTで遊んでみるか。
2026.02.24 15:40
MNISTの準備
・朝からちょっと頭痛がして全身がだるい。
・とりあえず気力でMNISTの手書き画像認識の下調べ。
・結局、あまりたいしたことはなかった。要するに画像データとそのデータの答え(ラベル:0〜9の数値)のファイルがあって、画像データの先頭16バイトとラベルの先頭8バイトがヘッダ。
・で、その後は画像データが28x28(1画素が1バイトの濃淡データ)、ラベルは1バイトで0〜9の値を示しているだけ。
・で、学習用のデータが6万セット、本番用のデータが1万セット用意されている。
・もちろん、使い方は自由なので、学習用のデータの先頭1000個で学習して次の1000個を学習結果の評価用としても良いわけだ。
train-images-idx3-ubyte.gz(6万セットの画像)
train-labels-idx1-ubyte.gz(6万セットのラベル)
t10k-images-idx3-ubyte.gz(1万セットの画像)
t10k-labels-idx1-ubyte.gz(1万セットのラベル)