Joker 1 year ago
parent
commit
caec07fdc3

BIN
day6/lenet/00_5.jpg


BIN
day6/lenet/01_0.jpg


BIN
day6/lenet/02_4.jpg


BIN
day6/lenet/03_1.jpg


BIN
day6/lenet/04_9.jpg


BIN
day6/lenet/05_2.jpg


BIN
day6/lenet/06_1.jpg


BIN
day6/lenet/07_3.jpg


BIN
day6/lenet/08_1.jpg


BIN
day6/lenet/09_4.jpg


BIN
day6/lenet/__pycache__/model.cpython-39.pyc


BIN
day6/lenet/datasets/MNIST/raw/t10k-images-idx3-ubyte


BIN
day6/lenet/datasets/MNIST/raw/t10k-images-idx3-ubyte.gz


BIN
day6/lenet/datasets/MNIST/raw/t10k-labels-idx1-ubyte


BIN
day6/lenet/datasets/MNIST/raw/t10k-labels-idx1-ubyte.gz


BIN
day6/lenet/datasets/MNIST/raw/train-images-idx3-ubyte


BIN
day6/lenet/datasets/MNIST/raw/train-images-idx3-ubyte.gz


BIN
day6/lenet/datasets/MNIST/raw/train-labels-idx1-ubyte


BIN
day6/lenet/datasets/MNIST/raw/train-labels-idx1-ubyte.gz


+ 28 - 0
day6/lenet/ds.py

@@ -0,0 +1,28 @@
+from torchvision.datasets import MNIST
+from torchvision.transforms import Compose, ToTensor
+import numpy as np
+import cv2
+
+# 加载数据集(如果不存在则下载)
+transform = Compose([ToTensor()]) # 把图像转换为float的张量
+ds_mnist = MNIST(root="datasets", train=True, download=True, transform=transform)
+
+# print(len(ds_mnist))
+
+# print(ds_mnist[0][0].shape)
+# print(type(ds_mnist))
+# print(ds_mnist[0][1])  # 类别
+
+# print(ds_mnist[0][0])
+
+# 把图像保存为文件
+for i in range(10):  
+    img, cls = ds_mnist[i]   # img是0-1之间float
+
+    img = img.mul(255)  # 0-255
+    img = img.numpy().copy()
+    img = img.astype(np.uint8) # 转为整数
+
+    # 转换成28 * 28 * 1的图像矩阵
+    img = img.transpose(1, 2, 0) # 1 * 28 * 28 -> 28 * 28 * 1
+    cv2.imwrite(F"{i:02d}_{cls}.jpg", img)

+ 75 - 0
day6/lenet/model.py

@@ -0,0 +1,75 @@
+from torch.nn import Module    # 扩展该类实现我们自己的深度网络模型
+from torch.nn import Conv2d, Linear   # 卷积运算(特征抽取),全邻接线性运算(分类器)
+from torch.nn.functional import relu, max_pool2d, avg_pool2d  # relu折线函数,maxpool(从数组返回一个最大值)
+import torch
+
+class LeNet5(Module):
+    # 构造器
+    def __init__(self, class_num=10):  # 10手写数字的分类。一共10个类别
+        super(LeNet5, self).__init__()
+        """
+            5层  (28 * 28 * 1)
+                |- 1. 卷积5 * 5 -> (28 * 28 * 6)    -(2, 2) -> (14, 14 , 6)
+                |- 2. 卷积5 * 5 -> (10 * 10 * 16)   -(2, 2) -> (5, 5, 16)
+                |- 3. 卷积5 * 5 -> (1 * 1 * 120)   
+                |- 4. 全连接 120 -> 84 
+                |- 5. 全连接 84 - 10 (1, 0, 0, 0, 0, 0, 0, 0, 0, 0)  取概率最大的下标就是识别出来的数字
+        """
+        self.conv1 = Conv2d(in_channels=1, out_channels=6,  kernel_size=5, stride=1, padding=2)
+        self.conv2 = Conv2d(in_channels=6, out_channels=16, kernel_size=5, stride=1, padding=0)
+        self.conv3 = Conv2d(in_channels=16, out_channels=120, kernel_size=5, stride=1,padding=0)
+
+        self.fc1 = Linear(120, 84)
+        self.fc2 = Linear(84, 10)
+
+
+    # 预测函数
+    def forward(self, x):
+        """
+            x表述输入图像, 格式是:[NHWC]
+        """
+        y = x
+        # 计算预测
+        # 第一层网络
+        y = self.conv1(y)  # (28*28*1) -> (28*28*6)
+        y = max_pool2d(y, (2, 2))  # (28*28*6) -> (14*14*6)
+        y = relu(y)        # 过滤负值(所有的负值设置为0)
+
+        # 第二层
+        y = self.conv2(y)   # (14*14*6) -> (10*10*16)
+        y = max_pool2d(y, (2, 2))   # (10*10*16) -> (5*5*16)
+        y = relu(y)         # 过滤负值
+
+        # 第三层
+        y = self.conv3(y)   # (5*5*16) -> (1*1*120)
+
+        # 把y从(1*1*120) -> (120)向量
+        y = y.view(-1, 120)  
+
+        # 第四层
+        y = self.fc1(y)
+        y = relu(y)
+
+        # 第五层
+        y = self.fc2(y)
+        # y = relu(y)        # 这个激活函数已经没有意义
+
+        # 把向量的分量全部转换0-1之间的值(概率)     
+        y = torch.softmax(y, dim=1) 
+
+        return y
+
+
+# print(__name__)
+if __name__ == "__main__":  # 表示是独立执行册程序块
+    # 下面代码被调用,则执行不到。
+    img = torch.randint(0, 256, (1, 1, 28, 28))  # 构造一个随机矩阵 == 噪音图像[NCHW]
+    img = img.float()    # 神经网络输入的必须是float类型
+    net = LeNet5()
+    y = net(img)
+    # y = net.forward(img)  # 等价于y = net(img)
+
+    # 判定最大下标
+    cls = torch.argmax(y, dim=1)
+    print(F"识别的结果是:{cls.numpy()[0]}")
+    print(y)

+ 107 - 0
day6/lenet/trian.py

@@ -0,0 +1,107 @@
+from model import LeNet5    # 引入我们写好的神经网络(也是我们要训练的网络)
+from torch.nn import CrossEntropyLoss  # 引入损失函数(用来做分类损失最佳)
+from torch.optim import Adam  # 损失函数优化器(其他损失函数的最小值:使用梯度下降法)
+from torchvision.datasets import MNIST # 数据集
+from torchvision.transforms import Compose, ToTensor  # 图像转换为张量
+from torch.utils.data import DataLoader # 批次数据集(数据集不是一次训练,而是分成多个批次)
+import torch  # 调用torch的基本函数
+import os   # 路径与文件处理
+
+class Trainer:
+    def __init__(self):
+        # 判定电脑安装GPU的环境:cuda,cnn,pytorch-
+        self.CUDA = False # torch.cuda.is_available()   # 返回逻辑值:True:支持GPU,False不支持GPU
+        # 准备训练需要的数据、损失函数,优化器,学习率
+        self.lr = 0.0001
+        self.m_file = "lenet5.pt"  # 模型的保存文件
+        self.net = LeNet5() # 需要训练的网络
+        if self.CUDA:
+            self.net.cuda()   # 把net网络模型存储到GPU上面
+
+        # 加载上一次训练的模型
+        if os.path.exists(self.m_file):
+            # 存在就加载
+            print("加载模型中...")
+            state = torch.load(self.m_file)  # 读取文件
+            self.net.load_state_dict(state)
+        else:
+            print("模型存在,重头训练")    
+        self.loss_f = CrossEntropyLoss()  # 损失函数
+        self.optimizer = Adam(self.net.parameters(), lr=self.lr)  # 优化器
+
+
+        # 数据集 - 训练集
+        self.trans = Compose([ToTensor()])
+        self.ds_train = MNIST(root="datasets", download=True, train=True, transform=self.trans)
+        self.bt_train = DataLoader(self.ds_train, batch_size=1000, shuffle=True) 
+
+        # 数据集 - 验证集
+        self.ds_valid = MNIST(root="datasets", download=True, train=False, transform=self.trans)
+        self.bt_valid = DataLoader(self.ds_valid, batch_size=1000, shuffle=False) #  shuffle=False是否随机打乱
+
+
+
+    def train_one(self):
+        # 训练一轮epoch,60批次batch,每个批次1000张录像
+        # 循环训练每个批次
+        batch = 1
+        for x, y in self.bt_train:
+            # print(F"\t|-第{batch:02d}批次训练。")
+            if self.CUDA:
+                x = x.cuda()
+                y = y.cuda()
+            y_ = self.net(x)  # 进行预测
+            # 计算误差
+            loss = self.loss_f(y_, y)   # 使用真实的标签与预测标签计算sunshi
+            # 优化卷积核与全链接矩阵
+            self.optimizer.zero_grad()
+            loss.backward()   # 使用导数计算梯度
+            self.optimizer.step()  # 更新梯度,优化网络模型
+            batch+=1
+
+    @torch.no_grad()   # 该函数中的运算都不会进行求导跟踪
+    def valid(self):
+        # 使用测试数据集验证 (准确率,损失值)
+        all_num = 0.0 # 验证的样本数
+        acc_num = 0.0 # 识别正确数量
+        all_loss = 0.0 # 累加每个样本的损失
+        for t_x, t_y in self.bt_valid:
+            if self.CUDA:
+                t_x = t_x.cuda()
+                t_y = t_y.cuda()
+            # 统计样本数
+            all_num  += len(t_x)
+            #  预测
+            t_y_ = self.net(t_x)
+            # 判定准确
+            y_cls = torch.argmax(t_y_, dim=1)
+            # 统计正确率
+            acc_num  += (y_cls == t_y).float().sum()
+            # 统计损失
+            all_loss += self.loss_f(t_y_, t_y)
+        print(F"\t|- 训练损失:{all_loss/all_num:8.6f}")
+        print(F"\t|- 准确率:{acc_num * 100.0/all_num:5.2f}%")
+
+
+
+    def train(self, epoch):
+        # 训练指定的论数
+        for e in range(epoch):
+            print(F"第{e:04d}轮训练。")
+            self.train_one()
+            self.valid()
+            # 保存训练的模型
+            torch.save(self.net.state_dict(), self.m_file)
+
+if __name__ == "__main__":
+    trainer = Trainer()
+    trainer.train(50) # 训练10轮
+    # print(torch.cuda.is_available()) 
+
+"""
+    244K = 6 * 5 * 5 * 8 矩阵  
+          16 * 5 * 5 * 8 
+         120 * 5 * 5 * 8
+             120 * 84* 8
+              84 * 10*8
+"""