LeNet
1、使用全連接層的侷限性:
圖像在同一列鄰近的像素在這個向量中可能相距較遠。它們構成的模式可能難以被模型識別。對於大尺寸的輸入圖像,使用全連接層容易導致模型過大。
2、使用卷積層的優勢:
卷積層保留輸入形狀。卷積層通過滑動窗口將同一卷積核與不同位置的輸入重複計算,從而避免參數尺寸過大。
LeNet分爲卷積層塊和全連接層塊兩個部分
卷積層塊裏的基本單位是卷積層後接平均池化層:卷積層用來識別圖像裏的空間模式,如線條和物體局部,之後的平均池化層則用來降低卷積層對位置的敏感性。
卷積層塊由兩個這樣的基本單位重複堆疊構成。在卷積層塊中,每個卷積層都使用 5×5 的窗口,並在輸出上使用sigmoid激活函數。第一個卷積層輸出通道數爲6,第二個卷積層輸出通道數則增加到16。
全連接層塊含3個全連接層。它們的輸出個數分別是120、84和10,其中10爲輸出的類別個數。
可以看到,在卷積層塊中輸入的高和寬在逐層減小。卷積層由於使用高和寬均爲5的卷積核,從而將高和寬分別減小4,而池化層則將高和寬減半,但通道數則從1增加到16。全連接層則逐層減少輸出個數,直到變成圖像的類別數10。
模型構造
讀入數據和數據預覽
batch_size = 256
train_iter, test_iter = load_data_fashion_mnist(
batch_size=batch_size, root='../data')
print(len(train_iter))
#數據展示
import matplotlib.pyplot as plt
def show_fashion_mnist(images, labels):
use_svg_display()
# 這裏的_表示我們忽略(不使用)的變量
_, figs = plt.subplots(1, len(images), figsize=(12, 12))
for f, img, lbl in zip(figs, images, labels):
f.imshow(img.view((28, 28)).numpy())
f.set_title(lbl)
f.axes.get_xaxis().set_visible(False)
f.axes.get_yaxis().set_visible(False)
plt.show()
定義device
def try_gpu():
"""If GPU is available, return torch.device as cuda:0; else return torch.device as cpu."""
if torch.cuda.is_available():
device = torch.device('cuda:0')
else:
device = torch.device('cpu')
return device
device = try_gpu()
定義網絡
# net
class Flatten(nn.Module): # 展平操作
def forward(self, x):
return x.view(x.shape[0], -1)
class Reshape(nn.Module): # 將圖像大小重定型
def forward(self, x):
return x.view(-1, 1, 28, 28) # (B x C x H x W)
net = torch.nn.Sequential( # LeNet
Reshape(),
nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, padding=2), # b*1*28*28 =>b*6*28*28
nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2), # b*6*28*28 =>b*6*14*14
nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5), # b*6*14*14 =>b*16*10*10
nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2), # b*16*10*10 => b*16*5*5
Flatten(), # b*16*5*5 => b*400
nn.Linear(in_features=16 * 5 * 5, out_features=120),
nn.Sigmoid(),
nn.Linear(120, 84),
nn.Sigmoid(),
nn.Linear(84, 10)
)
計算準確率
'''
(1). net.train()
啓用 BatchNormalization 和 Dropout,將BatchNormalization和Dropout置爲True
(2). net.eval()
不啓用 BatchNormalization 和 Dropout,將BatchNormalization和Dropout置爲False
'''
def evaluate_accuracy(data_iter, net,device=torch.device('cpu')):
"""Evaluate accuracy of a model on the given data set."""
acc_sum,n = torch.tensor([0],dtype=torch.float32,device=device),0
for X,y in data_iter:
# If device is the GPU, copy the data to the GPU.
X,y = X.to(device),y.to(device)
net.eval()
with torch.no_grad():
y = y.long()
acc_sum += torch.sum((torch.argmax(net(X), dim=1) == y)) #[[0.2 ,0.4 ,0.5 ,0.6 ,0.8] ,[ 0.1,0.2 ,0.4 ,0.3 ,0.1]] => [ 4 , 2 ]
n += y.shape[0]
return acc_sum.item()/n
訓練
def train_ch5(net, train_iter, test_iter, criterion, num_epochs, batch_size, device, lr=None):
"""Train and evaluate a model with CPU or GPU."""
print('training on', device)
net.to(device)
optimizer = optim.SGD(net.parameters(), lr=lr)
for epoch in range(num_epochs):
train_l_sum = torch.tensor([0.0], dtype=torch.float32, device=device)
train_acc_sum = torch.tensor([0.0], dtype=torch.float32, device=device)
n, start = 0, time.time()
for X, y in train_iter:
net.train()
optimizer.zero_grad()
X, y = X.to(device), y.to(device)
y_hat = net(X)
loss = criterion(y_hat, y)
loss.backward()
optimizer.step()
with torch.no_grad():
y = y.long()
train_l_sum += loss.float()
train_acc_sum += (torch.sum((torch.argmax(y_hat, dim=1) == y))).float()
n += y.shape[0]
test_acc = evaluate_accuracy(test_iter, net, device)
print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, '
'time %.1f sec'
% (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc,
time.time() - start))
# 訓練
lr, num_epochs = 0.9, 10
def init_weights(m):
if type(m) == nn.Linear or type(m) == nn.Conv2d:
torch.nn.init.xavier_uniform_(m.weight)
net.apply(init_weights)
net = net.to(device)
criterion = nn.CrossEntropyLoss() #交叉熵描述了兩個概率分佈之間的距離,交叉熵越小說明兩者之間越接近
train_ch5(net, train_iter, test_iter, criterion,num_epochs, batch_size,device, lr)
測試
# test
for testdata,testlabe in test_iter:
testdata,testlabe = testdata.to(device),testlabe.to(device)
break
print(testdata.shape,testlabe.shape)
net.eval()
y_pre = net(testdata)
print(torch.argmax(y_pre,dim=1)[:10])
print(testlabe[:10])
侷限
LeNet: 在大的真實數據集上的表現並不盡如⼈意。
1.神經網絡計算複雜。
2.還沒有⼤量深⼊研究參數初始化和⾮凸優化算法等諸多領域。
機器學習的特徵提取:手工定義的特徵提取函數
神經網絡的特徵提取:通過學習得到數據的多級表徵,並逐級表⽰越來越抽象的概念或模式。
神經網絡發展的限制:數據、硬件