一、概念
1. MLP
多層感知機(Multilayer Perceptron)縮寫為MLP,也稱作前饋神經(jīng)網(wǎng)絡(Feedforward Neural Network)。 它是一種基于神經(jīng)網(wǎng)絡的機器學習模型,通過多層非線性變換對輸入數(shù)據(jù)進行高級別的抽象和分類。
與單層感知機相比,MLP有多個隱藏層,每個隱藏層由多個神經(jīng)元組成,每個神經(jīng)元通過對上一層的輸入進行加權和處理,再通過激活函數(shù)進行非線性映射。
MLP的輸出層通常是一個 softmax 層,用于多分類任務,或者是一個 sigmoid 層,用于二分類任務。
由于它的強大表達能力和靈活性,MLP被廣泛應用于各種機器學習任務中。
2. 前向傳播
由于有多個層,參數(shù)需要在這些層之間傳遞。 首先需要實現(xiàn)的就是參數(shù)的前向傳播,計算過程如下:
- 將輸入數(shù)據(jù)傳遞給第一個隱藏層的神經(jīng)元;
- 對于每個神經(jīng)元,計算其加權和,即將輸入與對應的權重相乘并求和,再加上偏置項;
- 將加權和輸入到激活函數(shù)中,得到激活值,作為該神經(jīng)元的輸出;
- 將每個神經(jīng)元的輸出傳遞到下一層的神經(jīng)元,直至輸出層。
在這個過程中,數(shù)據(jù)和權重是前向傳播的主要傳播內容。
3. 反向傳播
利用鏈式法則對網(wǎng)絡中的參數(shù)進行梯度更新。 在訓練神經(jīng)網(wǎng)絡時,通常需要定義一個損失函數(shù)(loss function),用于評估模型預測結果與真實標簽之間的差距。 反向傳播算法的目標就是最小化這個損失函數(shù)。
在反向傳播過程中,算法首先計算損失函數(shù)對最后一層的輸出的梯度,然后根據(jù)鏈式法則逐層向前計算各層的梯度,并利用梯度下降法更新網(wǎng)絡中的參數(shù)。 具體地,算法會先將損失函數(shù)對輸出的梯度傳回網(wǎng)絡最后一層,然后依次向前計算各層的梯度。 在計算梯度的過程中,算法會利用反向傳播公式來計算當前層的梯度,然后將這個梯度傳遞到前一層。 在更新網(wǎng)絡參數(shù)時,算法會根據(jù)計算出的梯度和學習率來更新網(wǎng)絡中的權重和偏置。
通過不斷地反復迭代前向傳播和反向傳播兩個階段,可以不斷地更新網(wǎng)絡中的參數(shù),從而逐漸提高模型的性能。
下面是一個Python計算反向傳播的示例:
for epoch in range(num_epochs):
for x, y_true in zip(x_train, y_train):
# 前向傳播
hidden_layer = np.maximum(0, np.dot(x, self.weights1) + self.bias1) # ReLU激活函數(shù)
y_pred = np.dot(hidden_layer, self.weights2) + self.bias2
# 計算損失和梯度,使用均方誤差作為損失函數(shù)(Mean Squared Error,MSE)
# 對于每一個樣本,模型預測出來的輸出與實際輸出之間的差異會被平方,
# 然后對所有樣本的平方差進行求和并除以樣本數(shù),即可得到MSE作為模型的損失函數(shù)。
loss = np.square(y_true - y_pred).sum()
# 下面復雜的方法用來實現(xiàn)反向傳播
# 計算損失函數(shù)關于預測輸出的導數(shù)
d_loss_pred = -2.0 * (y_true - y_pred)
# 計算輸出層的梯度,
d_weights2 = np.dot(hidden_layer.reshape(-1, 1), d_loss_pred.reshape(1, -1))
# 計算輸出層偏置的梯度,其值等于輸出誤差
d_bias2 = d_loss_pred
# 計算隱藏層誤差,其中 self.weights2.T 代表輸出層權重的轉置,
# 計算得到的結果是一個行向量,代表每個隱藏層節(jié)點的誤差。
d_hidden = np.dot(d_loss_pred, self.weights2.T)
# 將隱藏層誤差中小于等于 0 的部分置為 0,相當于計算 ReLU 激活函數(shù)的導數(shù),
# 這是因為 ReLU 函數(shù)在小于等于 0 的部分導數(shù)為 0
d_hidden[hidden_layer <= 0] = 0 # ReLU激活函數(shù)的導數(shù)
# 計算隱藏層權重的梯度,
# 其中 x.reshape(-1, 1) 代表將輸入變?yōu)榱邢蛄浚?/span>
# d_hidden.reshape(1, -1) 代表將隱藏層誤差變?yōu)樾邢蛄浚?/span>
# 兩者的點積得到的是一個矩陣,
# 這個矩陣的行表示輸入的維度(也就是輸入節(jié)點的個數(shù)),
# 列表示輸出的維度(也就是隱藏層節(jié)點的個數(shù)),表示每個輸入和每個隱藏層節(jié)點的權重梯度。
d_weights1 = np.dot(x.reshape(-1, 1), d_hidden.reshape(1, -1))
# 計算隱藏層偏置的梯度,其值等于隱藏層誤差。
d_bias1 = d_hidden
# 更新權重和偏置
self.weights2 -= learning_rate * d_weights2
self.bias2 -= learning_rate * d_bias2
self.weights1 -= learning_rate * d_weights1
self.bias1 -= learning_rate * d_bias1
4. 評估模式與訓練模式
- 在訓練階段,需要調用 model.train() 來指定模型為訓練模式,以便啟用訓練中需要的特性,如 dropout 和 batch normalization 等。
- 如果在訓練過程中需要評估模型的性能,可以在評估前調用 model.eval(),以確保模型處于評估模式,而不是訓練模式。
- 在測試階段,需要調用 model.eval() 來指定模型為評估模式,以便禁用 dropout 和 batch normalization 等特性,以及啟用測試中需要的特性,如在計算中間層的輸出等。
- 在預測階段,需要調用 model.eval() 來指定模型為預測模式,以便禁用 dropout 和 batch normalization 等特性,并且只計算模型的前向傳播,以生成模型的輸出,而不更新模型的權重。
二、模型定義
1. 加載數(shù)據(jù)集
本示例使用 MNIST 數(shù)據(jù)集。
import torch
from torchvision import datasets, transforms
from torch.autograd import Variable
import time
import matplotlib.pyplot as plt
# 定義ToTensor和Normalize的transform
to_tensor = transforms.ToTensor()
normalize = transforms.Normalize((0.5,), (0.5,))
# 定義Compose的transform
transform = transforms.Compose([
to_tensor, # 轉換為張量
normalize # 標準化
])
# 下載數(shù)據(jù)集
data_train = datasets.MNIST(root="..//data//",
transform=transform,
train=True,
download=True)
data_test = datasets.MNIST(root="..//data//",
transform=transform,
train=False,
download=True)
# 裝載數(shù)據(jù)
data_loader_train = torch.utils.data.DataLoader(dataset=data_train,
batch_size=64,
shuffle=True)
data_loader_test = torch.utils.data.DataLoader(dataset=data_test,
batch_size=64,
shuffle=True)
2. 定義MLP層
下面定義一個有三個層的MLP。
對于這個MLP,它接收一個num_i的輸入,輸出為num_o的預測值。 隱藏層有2層,每層大小為num_h。
層的定義如下:
- self.linear1:輸入層到第一層隱藏層的線性轉換,其中num_i為輸入的特征數(shù),num_h為第一層隱藏層的特征數(shù)。
- self.relu:第一層隱藏層的激活函數(shù),采用ReLU。
- self.linear2:第一層隱藏層到第二層隱藏層的線性轉換,其中num_h為第一層隱藏層的特征數(shù),num_h為第二層隱藏層的特征數(shù)。
- self.relu2:第二層隱藏層的激活函數(shù),采用ReLU。
- self.linear3:第二層隱藏層到輸出層的線性轉換,其中num_h為第二層隱藏層的特征數(shù),num_o為輸出的特征數(shù)(或者說類別數(shù))。
class MLP(torch.nn.Module):
def __init__(self, num_i, num_h, num_o):
super(MLP, self).__init__()
self.linear1 = torch.nn.Linear(num_i, num_h)
self.relu = torch.nn.ReLU()
self.linear2 = torch.nn.Linear(num_h, num_h) # 2個隱層
self.relu2 = torch.nn.ReLU()
self.linear3 = torch.nn.Linear(num_h, num_o)
3. 前向傳播
def forward(self, x):
x = self.linear1(x)
x = self.relu(x)
x = self.linear2(x)
x = self.relu2(x)
x = self.linear3(x)
return x
在前向傳播時,輸入x先通過第一層的線性轉換,然后經(jīng)過第一層隱藏層的激活函數(shù),
再通過第二層的線性轉換,再經(jīng)過第二層隱藏層的激活函數(shù),
最后輸出預測值。
4. 優(yōu)化器
本文將使用PyTorch的優(yōu)化器工具用于反向傳播 。
優(yōu)化器(optimizer)是一個用于更新模型參數(shù)的工具,根據(jù)訓練集的損失函數(shù)(loss function)和反向傳播算法(backpropagation algorithm)計算梯度,并使用梯度下降算法(gradient descent algorithm)更新模型參數(shù),以最小化損失函數(shù)的值。
PyTorch提供了許多常用的優(yōu)化器,如隨機梯度下降法(SGD)、Adam、Adagrad、RMSprop等。
這些優(yōu)化器使用不同的更新策略,根據(jù)不同的訓練任務和數(shù)據(jù)集選擇合適的優(yōu)化器可以提高訓練效率和性能。
5. 反向傳播
本文使用和PyTorch優(yōu)化器的一個實例: torch.optim.Adam(),它使用反向傳播算法計算梯度并更新模型的權重,從而調整模型參數(shù)以最小化損失函數(shù)。
三、訓練
def train(model):
cost = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
# 設置迭代次數(shù)
epochs = 5
for epoch in range(epochs):
sum_loss = 0
train_correct = 0
for data in data_loader_train:
# 獲取數(shù)據(jù)和標簽
inputs, labels = data # inputs 維度:[64,1,28,28]
# 將輸入數(shù)據(jù)展平為一維向量
inputs = torch.flatten(inputs, start_dim=1) # 展平數(shù)據(jù),轉化為[64,784]
# 計算輸出
outputs = model(inputs)
# 將梯度清零
optimizer.zero_grad()
# 計算損失函數(shù)
loss = cost(outputs, labels)
# 反向傳播計算梯度
loss.backward()
# 使用優(yōu)化器更新模型參數(shù)
optimizer.step()
# 返回 outputs 張量每行中的最大值和對應的索引,1表示從行維度中找到最大值
_, id = torch.max(outputs.data, 1)
# 將每個小批次的損失值 loss 累加,用于最后計算平均損失
sum_loss += loss.data
# 計算每個小批次正確分類的圖像數(shù)量
train_correct += torch.sum(id == labels.data)
print('[%d/%d] loss:%.3f, correct:%.3f%%, time:%s' %
(epoch + 1, epochs, sum_loss / len(data_loader_train),
100 * train_correct / len(data_train),
time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())))
model.eval()
四、測試與預測
# 測試模型
def test(model, test_loader):
test_correct = 0
for data in test_loader:
inputs, lables = data
inputs, lables = Variable(inputs).cpu(), Variable(lables).cpu()
inputs = torch.flatten(inputs, start_dim=1) # 展并數(shù)據(jù)
outputs = model(inputs)
_, id = torch.max(outputs.data, 1)
test_correct += torch.sum(id == lables.data)
print(f'Accuracy on test set: {100 * test_correct / len(data_test):.3f}%')
五、預測
在神經(jīng)網(wǎng)絡模型的推斷(inference)階段中,我們不需要進行反向傳播,也不需要計算梯度,使用 with torch.no_grad(): 上下文管理器可以有效地減少內存消耗和計算時間
def predict(model, data):
model.eval()
with torch.no_grad():
output = model(data)
pred = output.data.max(1, keepdim=True)[1]
return pred
1234567
- output是模型在給定輸入數(shù)據(jù)后的輸出結果,每一行對應一個輸入數(shù)據(jù)樣本,每一列對應一個可能的輸出類別。
- output.data提取出了output中的數(shù)據(jù)部分,然后使用max()函數(shù)沿著第1個維度(即列)找到每一行中最大的值以及對應的索引。
- keepdim=True參數(shù)使得輸出結果保持和輸入數(shù)據(jù)output相同的維度。 因此,pred包含每個輸入樣本的預測類別,是一個包含預測標簽索引的一維張量
六、完整代碼
import torch
from torchvision import datasets, transforms
from torch.autograd import Variable
import time
import matplotlib.pyplot as plt
# 定義ToTensor和Normalize的transform
to_tensor = transforms.ToTensor()
normalize = transforms.Normalize((0.5,), (0.5,))
# 定義Compose的transform
transform = transforms.Compose([
to_tensor, # 轉換為張量
normalize # 標準化
])
# 下載數(shù)據(jù)集
data_train = datasets.MNIST(root="..//data//",
transform=transform,
train=True,
download=True)
data_test = datasets.MNIST(root="..//data//",
transform=transform,
train=False,
download=True)
# 裝載數(shù)據(jù)
data_loader_train = torch.utils.data.DataLoader(dataset=data_train,
batch_size=64,
shuffle=True)
data_loader_test = torch.utils.data.DataLoader(dataset=data_test,
batch_size=64,
shuffle=True)
class MLP(torch.nn.Module):
def __init__(self, num_i, num_h, num_o):
super(MLP, self).__init__()
self.linear1 = torch.nn.Linear(num_i, num_h)
self.relu = torch.nn.ReLU()
self.linear2 = torch.nn.Linear(num_h, num_h) # 2個隱層
self.relu2 = torch.nn.ReLU()
self.linear3 = torch.nn.Linear(num_h, num_o)
def forward(self, x):
x = self.linear1(x)
x = self.relu(x)
x = self.linear2(x)
x = self.relu2(x)
x = self.linear3(x)
return x
def train(model):
# 損失函數(shù),它將網(wǎng)絡的輸出和目標標簽進行比較,并計算它們之間的差異。在訓練期間,我們嘗試最小化損失函數(shù),以使輸出與標簽更接近
cost = torch.nn.CrossEntropyLoss()
# 優(yōu)化器的一個實例,用于調整模型參數(shù)以最小化損失函數(shù)。
# 使用反向傳播算法計算梯度并更新模型的權重。在這里,我們使用Adam優(yōu)化器來優(yōu)化模型。model.parameters()提供了要優(yōu)化的參數(shù)。
optimizer = torch.optim.Adam(model.parameters())
# 設置迭代次數(shù)
epochs = 2
for epoch in range(epochs):
sum_loss = 0
train_correct = 0
for data in data_loader_train:
inputs, labels = data # inputs 維度:[64,1,28,28]
# print(inputs.shape)
inputs = torch.flatten(inputs, start_dim=1) # 展平數(shù)據(jù),轉化為[64,784]
# print(inputs.shape)
outputs = model(inputs)
optimizer.zero_grad()
loss = cost(outputs, labels)
loss.backward()
optimizer.step()
_, id = torch.max(outputs.data, 1)
sum_loss += loss.data
train_correct += torch.sum(id == labels.data)
print('[%d/%d] loss:%.3f, correct:%.3f%%, time:%s' %
(epoch + 1, epochs, sum_loss / len(data_loader_train),
100 * train_correct / len(data_train),
time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())))
model.train()
# 測試模型
def test(model, test_loader):
model.eval()
test_correct = 0
for data in test_loader:
inputs, lables = data
inputs, lables = Variable(inputs).cpu(), Variable(lables).cpu()
inputs = torch.flatten(inputs, start_dim=1) # 展并數(shù)據(jù)
outputs = model(inputs)
_, id = torch.max(outputs.data, 1)
test_correct += torch.sum(id == lables.data)
print(f'Accuracy on test set: {100 * test_correct / len(data_test):.3f}%')
# 預測模型
def predict(model, data):
model.eval()
with torch.no_grad():
output = model(data)
pred = output.data.max(1, keepdim=True)[1]
return pred
num_i = 28 * 28 # 輸入層節(jié)點數(shù)
num_h = 100 # 隱含層節(jié)點數(shù)
num_o = 10 # 輸出層節(jié)點數(shù)
batch_size = 64
model = MLP(num_i, num_h, num_o)
train(model)
test(model, data_loader_test)
# 預測圖片,這里取測試集前10張圖片
for i in range(10):
# 獲取測試數(shù)據(jù)中的第一張圖片
test_image = data_test[i][0]
# 展平圖片
test_image = test_image.flatten()
# 增加一維作為 batch 維度
test_image = test_image.unsqueeze(0)
# 顯示圖片
plt.imshow(test_image.view(28, 28), cmap='gray')
plt.show()
pred = predict(model, test_image)
print('Prediction:', pred.item())
-
神經(jīng)網(wǎng)絡
+關注
關注
42文章
4733瀏覽量
100415 -
函數(shù)
+關注
關注
3文章
4277瀏覽量
62323 -
神經(jīng)元
+關注
關注
1文章
363瀏覽量
18431 -
機器學習
+關注
關注
66文章
8349瀏覽量
132312 -
MLP
+關注
關注
0文章
57瀏覽量
4215
發(fā)布評論請先 登錄
相關推薦
評論