順伝播 (順方向)
なぜアクティベーション関数があるのか
ここでは例として多層ニューラルネットワークを2層で表します。1層目の出力が2層目の入力になります。MMのW*X行列乗算、ADDはバイアスを加えるベクトル加算です。各層が線形変換だけであれば、何層を1層にまとめても(上図左側の式を参照)、層数に意味がないので、各層に非線形関数、つまり活性化関数を追加する必要があります。各層に独自の役割があることを確認します。
連鎖法則
逆方向伝播 (逆方向)
x が前層の出力の場合は、これも計算する必要がありますが、このとき、前層の出力 z に相当します。
損失 L から z への偏導関数は逆伝播から渡され、z から x への偏導関数は順伝播中に関数 f が計算されるときに計算される局所勾配です。
具体的な例を次に示します。
たとえば、順伝播では、関数 f=x*w の場合、z から x への偏導関数は w、z から w への偏導関数は x になります。x=2 と w=3 が渡されると、z=x*w=6 が出力されます。
次に、バックプロパゲーションで損失を取得し、L から z への偏微分を 5 として計算し、次に L から x への偏微分を 5*w として計算し、つまり 5*3=15 とします。同様に、L から w への偏微分は 10 となり、L から x への偏微分結果が引き続き上位層に渡されるため、各層は w の偏微分を通じて重み w を更新できます。
以上が順方向と逆方向の全体的なプロセスであり、w に対する損失の偏導関数を計算した後、勾配降下アルゴリズムを使用して w を更新できます。
上記は、勾配降下法アルゴリズムの公式導出です。
上記は確率的勾配降下法アルゴリズムの式です。
コードデモ:
リカレント ニューラル ネットワーク (RNN)
一般に、ニューラル ネットワークでは順序関係のあるデータを扱うことはできません。たとえば、畳み込みニューラル ネットワークを使用して画像を作成し、その画像のカテゴリを出力します。この出力は入力された画像にのみ関連しており、前の入力画像とは何の関係もありません。これは順序関係のないデータです。たとえば、自然言語と同様に、単語と文の間には明らかな文脈上の関係があり、それを処理するにはリカレント ニューラル ネットワークを使用する必要があります。
RNN セルは線形変換層です。上の図の右側にある 4 つの RNN セルはすべて同じ RNN セルを参照します。ここで、 h は hidden とも呼ばれます。各入力は前の出力とともに RNN セルに入力され、次のシーケンスのデータも一緒に取得されます。これが延々と続くため、各入力は前のシーケンスの結果を結合し、データ間のシーケンス関係を反映します。
たとえば、上図には 3 つの層があり、最初の層の RNN セルは元のデータ x と前のシーケンスの出力 h を受け取り、2 番目の層の RNN セルは前の層のシーケンスの出力 h と前のシーケンスの出力 h を受け取り、3 番目の層は 2 番目の層と同じです。
実践例
ここでは、hello->ohlol の法則を学ぶ必要があります。Seq はシーケンスの略です。
具体的な実装コード
import torch
input_size = 4
hidden_size = 3
batch_size = 1
#构建输入输出字典
idx2char_1 = ['e', 'h', 'l', 'o']
idx2char_2 = ['h', 'l', 'o']
x_data = [1, 0, 2, 2, 3]
y_data = [2, 0, 1, 2, 1]
# y_data = [3, 1, 2, 2, 3]
one_hot_lookup = [[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]]
#构造独热向量,此时向量维度为(SeqLen*InputSize)
x_one_hot = [one_hot_lookup[x] for x in x_data]
#view(-1……)保留原始SeqLen,并添加batch_size,input_size两个维度
inputs = torch.Tensor(x_one_hot).view(-1, batch_size, input_size)
#将labels转换为(SeqLen*1)的维度
labels = torch.LongTensor(y_data).view(-1, 1)
class Model(torch.nn.Module):
def __init__(self, input_size, hidden_size, batch_size):
super(Model, self).__init__()
self.batch_size = batch_size
self.input_size = input_size
self.hidden_size = hidden_size
self.rnncell = torch.nn.RNNCell(input_size = self.input_size,
hidden_size = self.hidden_size)
def forward(self, input, hidden):
# RNNCell input = (batchsize*inputsize)
# RNNCell hidden = (batchsize*hiddensize)
hidden = self.rnncell(input, hidden)
return hidden
#初始化零向量作为h0,只有此处用到batch_size
def init_hidden(self):
return torch.zeros(self.batch_size, self.hidden_size)
net = Model(input_size, hidden_size, batch_size)
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(net.parameters(), lr=0.1)
for epoch in range(15):
#损失及梯度置0,创建前置条件h0
loss = 0
optimizer.zero_grad()
hidden = net.init_hidden()
print("Predicted string: ",end="")
#inputs=(seqLen*batchsize*input_size) labels = (seqLen*1)
#input是按序列取的inputs元素(batchsize*inputsize)
#label是按序列去的labels元素(1)
for input, label in zip(inputs, labels):
hidden = net(input, hidden)
#序列的每一项损失都需要累加
loss += criterion(hidden, label)
#多分类取最大
_, idx = hidden.max(dim=1)
print(idx2char_2[idx.item()], end='')
loss.backward()
optimizer.step()
print(", Epoch [%d/15] loss = %.4f" % (epoch+1, loss.item()))