atm取款机的简单程序代码_LeNet:一个简单的卷积神经网络PyTorch实现
前兩篇文章分別介紹了卷積層和池化層,卷積和池化是卷積神經網絡必備的兩大基礎。本文我們將介紹一個早期用來識別手寫數字圖像的卷積神經網絡:LeNet[1]。LeNet名字來源于論文的第一作者Yann LeCun。1989年,LeNet使用卷積神經網絡和梯度下降法,使得手寫數字識別達到當時領先水平。這個奠基性的工作第一次將卷積神經網絡推上歷史舞臺,為世人所知。由于LeNet的出色表現,在很多ATM取款機上,LeNet被用來識別數字字符。
本文基于PyTorch和TensorFlow 2的代碼已經放在了我的GitHub上:https://github.com/luweizheng/machine-learning-notes/tree/master/neural-network/cnn。
網絡模型結構
LeNet的網絡結構如下圖所示。
LeNet分為卷積層塊和全連接層塊兩個部分。
卷積層塊里的基本單位是卷積層后接最大池化層:卷積層用來識別圖像里的空間模式,如線條和物體局部,之后的最大池化層則用來降低卷積層對位置的敏感性。卷積層塊由卷積層加池化層兩個這樣的基本單位重復堆疊構成。在卷積層塊中,每個卷積層都使用5×5的窗口,并在輸出上使用Sigmoid激活函數。整個模型的輸入是1維的黑白圖像,圖像尺寸為28×28。第一個卷積層輸出通道數為6,第二個卷積層輸出通道數則增加到16。這是因為第二個卷積層比第一個卷積層的輸入的高和寬要小,所以增加輸出通道使兩個卷積層的參數尺寸類似。卷積層塊的兩個最大池化層的窗口形狀均為2×2,且步幅為2。由于池化窗口與步幅形狀相同,池化窗口在輸入上每次滑動所覆蓋的區域互不重疊。
我們通過PyTorch的Sequential類來實現LeNet模型。
class LeNet(nn.Module): def __init__(self): super(LeNet, self).__init__() # 輸入 1 * 28 * 28 self.conv = nn.Sequential( # 卷積層1 # 在輸入基礎上增加了padding,28 * 28 -> 32 * 32 # 1 * 32 * 32 -> 6 * 28 * 28 nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, padding=2), nn.Sigmoid(), # 6 * 28 * 28 -> 6 * 14 * 14 nn.MaxPool2d(kernel_size=2, stride=2), # kernel_size, stride # 卷積層2 # 6 * 14 * 14 -> 16 * 10 * 10 nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5), nn.Sigmoid(), # 16 * 10 * 10 -> 16 * 5 * 5 nn.MaxPool2d(kernel_size=2, stride=2) ) self.fc = nn.Sequential( # 全連接層1 nn.Linear(in_features=16 * 5 * 5, out_features=120), nn.Sigmoid(), # 全連接層2 nn.Linear(in_features=120, out_features=84), nn.Sigmoid(), nn.Linear(in_features=84, out_features=10) ) def forward(self, img): feature = self.conv(img) output = self.fc(feature.view(img.shape[0], -1)) return output復制代碼我們有必要梳理一下模型各層的參數。輸入形狀為通道數為1的圖像(1維黑白圖像),尺寸為28×28,經過第一個5×5的卷積層,卷積時上下左右都使用了2個元素作為填充,輸出形狀為:(28 - 5 + 4 + 1) × (28 - 5 + 4 + 1) = 28 × 28。第一個卷積層輸出共6個通道,輸出形狀為:6 × 28 × 28。最大池化層核大小2×2,步幅為2,高和寬都被折半,形狀為:6 × 14 × 14。第二個卷積層的卷積核也為5 × 5,但是沒有填充,所以輸出形狀為:(14 - 5 + 1) × (14 - 5 + 1) = 10 × 10。第二個卷積核的輸出為16個通道,所以變成了 16 × 10 × 10。經過最大池化層后,高和寬折半,最終為:16 × 5 × 5。
卷積層塊的輸出形狀為(batch_size, output_channels, height, width),在本例中是(batch_size, 16, 5, 5),其中,batch_size是可以調整大小的。當卷積層塊的輸出傳入全連接層塊時,全連接層塊會將一個batch中每個樣本變平(flatten)。原來是形狀是:(通道數 × 高 × 寬),現在直接變成一個長向量,向量長度為通道數 × 高 × 寬。在本例中,展平后的向量長度為:16 × 5 × 5 = 400。全連接層塊含3個全連接層。它們的輸出個數分別是120、84和10,其中10為輸出的類別個數。
訓練模型
基于上面的網絡,我們開始訓練模型。我們使用Fashion-MNIST作為訓練數據集,很多框架,比如PyTorc提供了Fashion-MNIST數據讀取的模塊,我做了一個簡單的封裝:
def load_data_fashion_mnist(batch_size, resize=None, root='~/Datasets/FashionMNIST'): """Use torchvision.datasets module to download the fashion mnist dataset and then load into memory.""" trans = [] if resize: trans.append(torchvision.transforms.Resize(size=resize)) trans.append(torchvision.transforms.ToTensor()) transform = torchvision.transforms.Compose(trans) mnist_train = torchvision.datasets.FashionMNIST(root=root, train=True, download=True, transform=transform) mnist_test = torchvision.datasets.FashionMNIST(root=root, train=False, download=True, transform=transform) if sys.platform.startswith('win'): num_workers = 0 else: num_workers = 4 train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers) test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers) return train_iter, test_iter復制代碼load_data_fashion_mnist()方法返回訓練集和測試集。
在訓練過程中,我們希望看到每一輪迭代的準確度,構造一個evaluate_accuracy方法,計算當前一輪迭代的準確度(模型預測值與真實值之間的誤差大小):
def evaluate_accuracy(data_iter, net, device=None): if device is None and isinstance(net, torch.nn.Module): device = list(net.parameters())[0].device acc_sum, n = 0.0, 0 with torch.no_grad(): for X, y in data_iter: if isinstance(net, torch.nn.Module): # set the model to evaluation mode (disable dropout) net.eval() # get the acc of this batch acc_sum += (net(X.to(device)).argmax(dim=1) == y.to(device)).float().sum().cpu().item() # change back to train mode net.train() n += y.shape[0] return acc_sum / n復制代碼接著,我們可以構建一個train()方法,用來訓練神經網絡:
def try_gpu(i=0): if torch.cuda.device_count() >= i + 1: return torch.device(f'cuda:{i}') return torch.device('cpu')def train(net, train_iter, test_iter, batch_size, optimizer, num_epochs, device=try_gpu()): net = net.to(device) print("training on", device) loss = torch.nn.CrossEntropyLoss() batch_count = 0 for epoch in range(num_epochs): train_l_sum, train_acc_sum, n = 0.0, 0.0, 0 for X, y in train_iter: X = X.to(device) y = y.to(device) y_hat = net(X) l = loss(y_hat, y) optimizer.zero_grad() l.backward() optimizer.step() train_l_sum += l.cpu().item() train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item() n += y.shape[0] batch_count += 1 test_acc = evaluate_accuracy(test_iter, net) if epoch % 10 == 0: print(f'epoch {epoch + 1} : loss {train_l_sum / batch_count:.3f}, train acc {train_acc_sum / n:.3f}, test acc {test_acc:.3f}')復制代碼在整個程序的主邏輯中,設置必要的參數,讀入訓練和測試數據并開始訓練:
def main(): batch_size = 256 lr, num_epochs = 0.9, 100 net = LeNet() optimizer = torch.optim.SGD(net.parameters(), lr=lr) # load data train_iter, test_iter = load_data_fashion_mnist(batch_size=batch_size) # train train(net, train_iter, test_iter, batch_size, optimizer, num_epochs)復制代碼小結
參考文獻
總結
以上是生活随笔為你收集整理的atm取款机的简单程序代码_LeNet:一个简单的卷积神经网络PyTorch实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: bcp 不能调用where 子句_MyS
- 下一篇: python pyecharts 折线图