關於概念:
BRNN連接兩個相反的隱藏層到同一個輸出.基於生成性深度學習,輸出層能夠同時的從前向和後向接收信息.該架構是1997年被Schuster和Paliwal提出的.引入BRNNS是爲了增加網絡所用的輸入信息量.例如,多層感知機(MLPS)和延時神經網絡(TDNNS)在輸入數據的靈活性方面是非常有侷限性的.因爲他們需要輸入的數據是固定的.標準的遞歸神靜
網絡也有侷限,就是將來的數據數據不能用現在狀態來表達.BRNN恰好能夠彌補他們的劣勢.它不需要輸入的數據固定,與此同時,將來的輸入數據也能從現在的狀態到達.
BRNN的原理是將正則RNN的神經元分成兩個方向。一個用於正時方向(正向狀態),另一個用於負時間方向(反向狀態).這兩個狀態的輸出沒有連接到相反狀態的輸入。通過這兩個時間方向,可以使用來自當前時間幀的過去和將來作爲輸入信息。
雙向RNN的思想和原始版RNN有一些許不同,只要是它考慮到當前的輸出不止和之前的序列元素有關係,還和之後的序列元素也是有關係的。舉個例子來說,如果我們現在要去填一句話中空缺的詞,那我們直觀就會覺得這個空缺的位置填什麼詞其實和前後的內容都有關係,對吧。雙向RNN其實也非常簡單,我們直觀理解一下,其實就可以把它們看做2個RNN的疊加。輸出的結果這個 時候就是基於2個RNN的隱狀態計算得到的。
關於訓練:
BRNNS可以使用RNNS類似的算法來做訓練.因爲兩個方向的神經元沒有任何相互作用。然而,當應用反向傳播時,由於不能同時更新輸入和輸出層,因此需要額外的過程。訓練的一般流程如下:對於前向傳遞,先傳遞正向狀態和後向狀態,然後輸出神經元通過.對於後向傳遞,首先輸出神經元,然後傳遞正向狀態和後退狀態。在進行前向和後向傳遞之後,更新權重。
關於源碼:
首先看一下BRNN的定義,定義中使用了兩層的網絡,使用的模型是nn.LSTM.這裏的LSTM是一類可以處理長期依賴問題的特殊的RNN,由Hochreiter 和 Schmidhuber於1977年提出,目前已有多種改進,且廣泛用於各種各樣的問題中。LSTM主要用來處理長期依賴問題,與傳統RNN相比,長時間的信息記憶能力是與生俱來的。參數bidirectional=True是表示
該網路是一個雙向的網絡.這裏的參數batch_first=True,因爲nn.lstm()接受的數據輸入是(序列長度,batch,輸入維數),這和我們cnn輸入的方式不太一致,所以使用batch_first,我們可以將輸入變成(batch,序列長度,輸入維數)
# Bidirectional recurrent neural network (many-to-one) class BiRNN(nn.Module): def __init__(self, input_size, hidden_size, num_layers, num_classes): super(BiRNN, self).__init__() self.hidden_size = hidden_size self.num_layers = num_layers self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True, bidirectional=True) self.fc = nn.Linear(hidden_size*2, num_classes) # 2 for bidirection def forward(self, x): # Set initial states h0 = torch.zeros(self.num_layers*2, x.size(0), self.hidden_size).to(device) # 2 for bidirection c0 = torch.zeros(self.num_layers*2, x.size(0), self.hidden_size).to(device) # Forward propagate LSTM out, _ = self.lstm(x, (h0, c0)) # out: tensor of shape (batch_size, seq_length, hidden_size*2) # Decode the hidden state of the last time step out = self.fc(out[:, -1, :]) return out
在實現函數中,首先設置初始化的狀態:h0,c0,然後根據初始化的狀態來輸出決策後的內容,把結果線性插值法過濾後輸出.
這個神經網絡的其他部分使用和別的網絡是一樣的,訓練部分和測試就不再一一介紹了,想知道的朋友可以參考我前面的文章的介紹.下面給出整體的源碼:
最終的可運行源碼:
1 import torch 2 import torch.nn as nn 3 import torchvision 4 import torchvision.transforms as transforms 5 6 7 # Device configuration 8 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 9 10 # Hyper-parameters 11 input_size = 784 12 hidden_size = 500 13 num_classes = 10 14 #input_size = 84 15 #hidden_size = 50 16 #num_classes = 2 17 num_epochs = 5 18 batch_size = 100 19 learning_rate = 0.001 20 21 # MNIST dataset 22 train_dataset = torchvision.datasets.MNIST(root='../../data', 23 train=True, 24 transform=transforms.ToTensor(), 25 download=True) 26 27 test_dataset = torchvision.datasets.MNIST(root='../../data', 28 train=False, 29 transform=transforms.ToTensor()) 30 31 # Data loader 32 train_loader = torch.utils.data.DataLoader(dataset=train_dataset, 33 batch_size=batch_size, 34 shuffle=True) 35 36 test_loader = torch.utils.data.DataLoader(dataset=test_dataset, 37 batch_size=batch_size, 38 shuffle=False) 39 40 # Fully connected neural network with one hidden layer 41 class NeuralNet(nn.Module): 42 def __init__(self, input_size, hidden_size, num_classes): 43 super(NeuralNet, self).__init__() 44 self.fc1 = nn.Linear(input_size, hidden_size) 45 self.relu = nn.ReLU() 46 self.fc2 = nn.Linear(hidden_size, num_classes) 47 48 def forward(self, x): 49 out = self.fc1(x) 50 out = self.relu(out) 51 out = self.fc2(out) 52 return out 53 54 model = NeuralNet(input_size, hidden_size, num_classes).to(device) 55 56 # Loss and optimizer 57 criterion = nn.CrossEntropyLoss() 58 optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) 59 60 # Train the model 61 total_step = len(train_loader) 62 for epoch in range(num_epochs): 63 for i, (images, labels) in enumerate(train_loader): 64 # Move tensors to the configured device 65 images = images.reshape(-1, 28*28).to(device) 66 labels = labels.to(device) 67 68 # Forward pass 69 outputs = model(images) 70 loss = criterion(outputs, labels) 71 72 # Backward and optimize 73 optimizer.zero_grad() 74 loss.backward() 75 optimizer.step() 76 77 if (i+1) % 100 == 0: 78 print ('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}' 79 .format(epoch+1, num_epochs, i+1, total_step, loss.item())) 80 # Test the model 81 # In test phase, we don't need to compute gradients (for memory efficiency) 82 with torch.no_grad(): 83 correct = 0 84 total = 0 85 for images, labels in test_loader: 86 images = images.reshape(-1, 28*28).to(device) 87 labels = labels.to(device) 88 outputs = model(images) 89 _, predicted = torch.max(outputs.data, 1) 90 total += labels.size(0) 91 #print(predicted) 92 correct += (predicted == labels).sum().item() 93 94 print('Accuracy of the network on the 10000 test images: {} %'.format(100 * correct / total)) 95 96 # Save the model checkpoint 97 torch.save(model.state_dict(), 'model.ckpt') 98
結果這裏就不再貼出來了,想知道的朋友可以自己運行一下.
參考文檔: