李宏毅作业3!!!!!食物分类 我好饿啊啊啊啊啊!

写在前面的话。

1,不知道大家是什么时候写自己的第一个神经网络的呢 ? 我不知道我是不是正常,我面对作业题目,会有一种完全不知如何下手的感觉。

可能知道步骤可能是读文件,取数据,建立网络,学习,结束。但是每一步我都不会,只能屁颠屁颠看大佬的代码,还只求能看懂。呜呜呜。

2,我发现有时候网络错误 真的不是人家网络的错,明明是你给的图片自己就没法分别哎 ,比如下面这俩图片  都是两部分组成的,你让人家

怎么学习?要不你自己来试试?

 

 

 

 

3 感觉人工智能的世界正在逐步向我解开神秘的面纱,冲啊!!!!!!!!!!!!!!!!!!!

 

  这次依然是抄代码啦  抄的是这位的 :https://blog.csdn.net/iteapoy/article/details/105765411?spm=1001.2014.3001.5501、

import os
import numpy as np
import cv2
import torch
import torch.nn as nn
import torchvision.transforms as transforms
import pandas as pd
from torch.utils.data import DataLoader, Dataset
import time

  额  上面的 我到现在都不知道导入哪些,反正全import了 准没错

#定义一个读取图片的函数readfile()
def readfile(path, label):
# label 是一个布尔值,代表需不需要返回 y 值
image_dir = sorted(os.listdir(path)) #(os.listdir返回的是指定文件夹下文件和文件夹的列表)
# x存储图片,每张彩色图片都是128(高)*128(宽)*3(彩色三通道)
x = np.zeros((len(image_dir), 128, 128, 3), dtype=np.uint8) #四维空间好难想啊 沃日!
# y存储标签,每个y大小为1
y = np.zeros((len(image_dir)), dtype=np.uint8)
for i, file in enumerate(image_dir): #enumerate将列表组合为索引序列。其实就是想读出来一个i
img = cv2.imread(os.path.join(path, file)) #os.path.join是路径拼接函数 ,就是定位图片地址。
# 利用cv2.resize()函数将不同大小的图片统一为128(高)*128(宽) imread 会返回每一张图片的 高度,宽度 通道。
x[i, :, :] = cv2.resize(img,(128, 128))
if label:
y[i] = int(file.split("_")[0])
if label:
return x, y
else:
return x

  那么 从这里开始 我以后遇到文件 都要定义一个读文件的函数 确实看起来会好很多。

#分别将 training set、validation set、testing set 用函数 readfile() 读进来
workspace_dir = './food-11'
print("Reading data")
train_x, train_y = readfile(os.path.join(workspace_dir, "training"), True)
print("Size of training data = {}".format(len(train_x)))
val_x, val_y = readfile(os.path.join(workspace_dir, "validation"), True)
print("Size of validation data = {}".format(len(val_x)))
test_x = readfile(os.path.join(workspace_dir, "testing"), False)
print("Size of Testing data = {}".format(len(test_x)))

  嘟嘟嘟

#training 时,通过随机旋转、水平翻转图片来进行数据增强(data augmentation)
train_transform = transforms.Compose([                   #compose可以将多个步骤整合在一起。
    transforms.ToPILImage(),                      #转换一个张量到图片
    transforms.RandomHorizontalFlip(), #以0.5的概率水平反转图像随机翻转图片,概率可以调
    transforms.RandomRotation(15), #随机旋转图片一定的角度(在-15到15之间)
    transforms.ToTensor(), #将图片变成 Tensor,并且把数值normalize到[0,1]
])
#testing 时,不需要进行数据增强(data augmentation)
test_transform = transforms.Compose([
    transforms.ToPILImage(),
    transforms.ToTensor(),
])
class ImgDataset(Dataset):
    def __init__(self, x, y=None, transform=None):
        self.x = x
        # label 需要是 LongTensor 型
        self.y = y
        if y is not None:
            self.y = torch.LongTensor(y)
        self.transform = transform
    def __len__(self):
        return len(self.x)
    def __getitem__(self, index):
        X = self.x[index]
        if self.transform is not None:
            X = self.transform(X)
        if self.y is not None:
            Y = self.y[index]
            return X, Y
        else:
            return X

##即 定义函数时 需要考虑 数据集和测试集的问题。

  我知道你定义了一个类,也知道你定义类是为了数据转换,那么问题来了  我在什么时候需要定义类呢? 为什么我不是定义的函数呢?

batch_size = 128
train_set = ImgDataset(train_x, train_y, train_transform)
val_set = ImgDataset(val_x, val_y, test_transform)
train_loader = DataLoader(train_set, batch_size=batch_size, shuffle=True) #shuffle是否重新洗牌的意思。
val_loader = DataLoader(val_set, batch_size=batch_size, shuffle=False)

  DataLoader 会用的很多很多,作用其实就是感觉将数据分为以一批一批的。

 

class Classifier(nn.Module):
    def __init__(self):
        super(Classifier, self).__init__()          #e额 怎么去看nn.module里有啥东西啊
        #torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding)
        #torch.nn.MaxPool2d(kernel_size, stride, padding)
        #input 维度 [3, 128, 128]
        self.cnn = nn.Sequential(
            nn.Conv2d(3, 64, 3, 1, 1),  # 输出[64, 128, 128] conv2d 是二维卷积。第一个三是三通道 64是输出通道数。第二个三是 3乘以3的卷积框
            nn.BatchNorm2d(64),          #正则化标准化
            nn.ReLU(),                   #线性整流
            nn.MaxPool2d(2, 2, 0),      # 输出[64, 64, 64]

            nn.Conv2d(64, 128, 3, 1, 1), # 输出[128, 64, 64]
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(2, 2, 0),      # 输出[128, 32, 32]

            nn.Conv2d(128, 256, 3, 1, 1), # 输出[256, 32, 32]
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(2, 2, 0),      # 输出[256, 16, 16]

            nn.Conv2d(256, 512, 3, 1, 1), # 输出[512, 16, 16]
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2, 2, 0),       # 输出[512, 8, 8]

            nn.Conv2d(512, 512, 3, 1, 1), # 输出[512, 8, 8]
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2, 2, 0),       # 输出[512, 4, 4]
        )
        # 全连接的前向传播神经网络
        self.fc = nn.Sequential(
            nn.Linear(512*4*4, 1024),   #1024的全连接。
            nn.ReLU(),
            nn.Linear(1024, 512),      #第二个全连接 512的结点
            nn.ReLU(),
            nn.Linear(512, 11)   # 最后是11个分类
        )

    def forward(self, x):
        out = self.cnn(x)
        out = out.view(out.size()[0], -1)  # 摊平成1维
        return self.fc(out)

  额  专门去学了 calss是个什么玩意。。————init  super  都是class的固定写法,

这里就定义了整个网络的框架是什么样的  我看出来了  一般来说  init里面会定义一些单元。例如 卷积啊 BN啊 这些单元的参数都定义好  然后在 forward里面去调用他们形成网络。

其实__init__里也可以不用sequential,这样forward里面就可以聚集超级一大堆的网络了 。

model = Classifier().cuda() #用cuda加速
loss = nn.CrossEntropyLoss() # 因为是分类任务,所以使用交叉熵损失
optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # 使用Adam优化器
num_epoch = 30 #迭代次数

for epoch in range(num_epoch):
    epoch_start_time = time.time()
    train_acc = 0.0
    train_loss = 0.0
    val_acc = 0.0
    val_loss = 0.0

    model.train() # 确保 model 是在 训练 model (开启 Dropout 等...)
    for i, data in enumerate(train_loader):
        optimizer.zero_grad() # 用 optimizer 将模型参数的梯度 gradient 归零
        train_pred = model(data[0].cuda()) # 利用 model 得到预测的概率分布,这边实际上是调用模型的 forward 函数
        batch_loss = loss(train_pred, data[1].cuda()) # 计算 loss (注意 prediction 跟 label 必须同时在 CPU 或是 GPU 上)
        batch_loss.backward() # 利用 back propagation 算出每个参数的 gradient
        optimizer.step() # 以 optimizer 用 gradient 更新参数

        train_acc += np.sum(np.argmax(train_pred.cpu().data.numpy(), axis=1) == data[1].numpy())
        train_loss += batch_loss.item()

    #验证集val
    model.eval()
    with torch.no_grad():
        for i, data in enumerate(val_loader):
            val_pred = model(data[0].cuda())
            batch_loss = loss(val_pred, data[1].cuda())

            val_acc += np.sum(np.argmax(val_pred.cpu().data.numpy(), axis=1) == data[1].numpy())
            val_loss += batch_loss.item()

        #将结果 print 出來
        print('[%03d/%03d] %2.2f sec(s) Train Acc: %3.6f Loss: %3.6f | Val Acc: %3.6f loss: %3.6f' % \
              (epoch + 1, num_epoch, time.time()-epoch_start_time, \
               train_acc/train_set.__len__(), train_loss/train_set.__len__(), val_acc/val_set.__len__(), val_loss/val_set.__len__()))

  

 这里一般操作 先让model等于之前定义的网络 也就是那个CLASS,然后定义loss ,optimizer eopch等等这些 。还要把他们都放到gpu上CUDA。

好像优化就几步  1 optimizer.zero_grad()  2 计算loss  3 用LOSS进行反向传播,, loss.backward 4  更新优化  optimizer.step()

 

 下面是resnet时间  基于MNIST的 其实相对于上面的就改了一点点的东西。

import torch
from torch import nn
from torchvision import transforms
from torch.utils.data import DataLoader, Dataset
from torch import optim
import os
import csv
from PIL import Image
from torchvision import datasets
import time
from torch.nn import functional as F
import numpy as np
import matplotlib.pyplot as plt

  不说了 

start_time = time.time()
trans = transforms.Compose(
    (transforms.Resize((32, 32)), transforms.ToTensor()))
# trans = transforms.Compose(transforms.ToTensor())
train_set = datasets.MNIST(
    root='F:\\li_XIANGMU\\MNIST',
    train=True,
    transform=trans)
val_set = list(
    datasets.MNIST(
        'F:\\li_XIANGMU\\MNIST',
        train=False,
        transform=trans))[
            :5000]
test_set = list(
    datasets.MNIST(
        'F:\\li_XIANGMU\\MNIST',
        train=False,
        transform=trans))[
            5000:]

train_loader = DataLoader(train_set, batch_size=150, shuffle=True)
val_loader = DataLoader(val_set, batch_size=50, shuffle=True)
test_loader = DataLoader(test_set, batch_size=50, shuffle=True)

  

  我用了pep8  结果就变成这个样子了 说实话 我也不知道好不好看 

 

 

# 搭建残差块


class res_block(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1, down_sample=None):
        super(res_block, self).__init__()
        self.conv_1 = nn.Conv2d(
            in_channels,
            out_channels,
            kernel_size=3,
            stride=stride,
            padding=1)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
        self.conv_2 = nn.Conv2d(
            out_channels,
            out_channels,
            kernel_size=3,
            stride=1,
            padding=1)
        self.bn2 = nn.BatchNorm2d(out_channels)
        self.down_sample = down_sample

    def forward(self, x):
        residual = x
        out = self.conv_1(x)
        out = self.bn1(out)
        out = self.relu(out)
        out = self.conv_2(out)
        out = self.bn2(out)

        if self.down_sample:
            residual = self.down_sample(x)
        out += residual
        out = self.relu(out)

        return out

  一个块是 卷积 +BN+RELU+卷积+BN+残差+RELU

# 搭建残差网络
class ResNet(nn.Module):
    def __init__(self, block, layers, num_class=10):
        super(ResNet, self).__init__()
        self.conv_1 = nn.Conv2d(
            in_channels=1,
            out_channels=16,
            kernel_size=3,
            stride=3,
            padding=0)
        self.in_channels = 16
        self.bn = nn.BatchNorm2d(16)
        self.relu = nn.ReLU(inplace=True)  # inplace=True 意思是原地操作 无新变量
        self.layer1 = self.make_layer(block, 32, layers[0], 1)
        self.layer2 = self.make_layer(block, 64, layers[1], stride=1)
        self.layer3 = self.make_layer(block, 128, layers[2], stride=2)
        self.layer4 = self.make_layer(block, 256, layers[3], stride=2)
        # self.avg_pool = nn.AvgPool2d(2)
        self.fc = nn.Linear(256 * 3 * 3, num_class)

    def make_layer(self, block, out_channels, blocks, stride=1):
        down_sample = None
        # if (stride != 1) or (self.in_channels != out_channels):
        if self.in_channels != out_channels:
            down_sample = nn.Sequential(
                nn.Conv2d(
                    self.in_channels,
                    out_channels,
                    kernel_size=1,
                    stride=stride),
                nn.BatchNorm2d(out_channels)
            )

        layers = []
        layers.append(
            block(
                self.in_channels,
                out_channels,
                stride,
                down_sample))
        self.in_channels = out_channels
        for i in range(1, blocks):
            layers.append(block(out_channels, out_channels))
        return nn.Sequential(*layers)

    def forward(self, x):
        out = self.conv_1(x)
        out = self.bn(out)
        out = self.relu(out)
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = self.layer4(out)
        # out = self.avg_pool(out)
        out = out.view(out.size()[0], -1)
        out = self.fc(out)
        return out

  这里残差网络了 可以通过数组来控制每层残差块数量  如果是【2,2,2,2】就是res18了 

device = torch.device('cuda')
model = ResNet(res_block,[1,1,1,1]).to(device)
print('模型需要训练的参数共有{}个'.format(sum(map(lambda p: p.numel(), model.parameters()))))
loss = nn.CrossEntropyLoss()
learning_rate = 1e-3
optimizer = optim.Adam(model.parameters(), lr=learning_rate)

num_epoch = 10
plt_train_loss = []
plt_val_loss = []
plt_train_acc = []
plt_val_acc = []

  跟上面一样 但是我没搞懂那个参数怎么计算的 

 

best_epoch, best_acc = 0, 0
for epoch in range(num_epoch):
    epoch_start_time = time.time()
    train_acc = 0.0
    train_loss = 0.0
    val_acc = 0.0
    val_loss = 0.0
    # checkpoint = {
    #     "net": model.state_dict(),
    #     'optimizer':optimizer.state_dict(),
    #     "epoch":epoch
    # }    #保存用的
    model.train()
    for i, (img,label) in enumerate(train_loader):
        img, label = img.to(device), label.to(device)
        optimizer.zero_grad()
        train_pred = model(img)
        batch_loss = loss(train_pred, label)
        batch_loss.backward()
        optimizer.step()

        train_acc += np.sum(np.argmax(train_pred.cpu().data.numpy(),
                            axis=1) == label.cpu().numpy())
        #这里必须都移动到CPU上才能进行numpy的操作。
        train_loss += batch_loss.item()
    val_acc, val_loss = evaluate(model, val_loader)
    val_loss += val_loss.item()
    if val_acc > best_acc:
        print('验证集上的准确率是:{}'.format(val_acc))
        best_epoch = epoch
        best_acc = val_acc
        torch.save(model.state_dict(), 'checkpoint.txt')
        # 将结果 print 出來



    # 保存用于画图
    plt_train_acc.append(train_acc / train_set.__len__())
    plt_train_loss.append(train_loss / train_set.__len__())
    plt_val_acc.append(val_acc)
    plt_val_loss.append(val_loss/val_set.__len__())
    print('[%03d/%03d] %2.2f sec(s) Train Acc: %3.6f Loss: %3.6f | Val Acc: %3.6f loss: %3.6f' %
          (epoch + 1, num_epoch, time.time() - epoch_start_time,
           plt_train_acc[-1], plt_train_loss[-1], plt_val_acc[-1], plt_val_loss[-1]))
# Accuracy曲线
plt.plot(plt_train_acc)
plt.plot(plt_val_acc)
plt.title('Accuracy')
plt.legend(['train', 'val'])
plt.savefig('acc.png')
plt.show()


print('best_acc:{},best_epoch:{}'.format(best_acc, best_epoch))
model.load_state_dict(torch.load('checkpoint.txt'))
print('测试集')
test_acc, test_loss= evaluate(model, test_loader)
print('测试集准确率为:{}'.format(test_acc))

end_time = time.time()
print("总共用时 %2.2f sec(s)" % (end_time-start_time))

  学习和 测试 准确度好高

resnet参数主要来自卷积核  3*3*INCHANNELS*OUTCHANNELS +OUT_CHANNELS 这样子  最后那个加的是卷积上的BIAS

 

 

话说我本来写这段代码时  有很多很多想说的 可是到最后都忘了  下次应该想到就记下来的,

posted @ 2021-06-02 18:22  yi术家  阅读(735)  评论(0)    收藏  举报