深度学习-学习记录1

深度学习-学习记录

一.如何搭建一个神经网络

一些概念

  • 激活函数:用途是将一个无边界的输入,转变成一个可预测的形式。常用的激活函数就就是S型函数:

S型函数的值域是(0, 1)。简单来说,就是把(−∞, +∞)压缩到(0, 1) ,很大的负数约等于0,很大的正数约等于1。

  • 给定输入,得到输出的过程被称为前馈(feedforward)

  • 神经网络的基本单位:神经元,接受输入,对其做一些数据操作,然后产生输出

  • 在神经网络的前向传播过程中,输入信号通过加权和计算后,再加上一个偏置值,被送入激活函数中进行激活操作

  • 权重向量:用来计算输入和输出之间的映射关系,通常是一个一维向量或矩阵

  • 偏置值(截距项)的作用是调整神经元的激活阈值,也可以看做是引入了一个额外的信号

  • 隐藏单元:不直接与外界交互的,位于输入层和输出层之间的所有神经元;通过调整隐藏单元中的权重向量和偏置值,可以达到分类和预测的目的

  • 关于self参数和__ init __()函数:

  • __ init __()函数在类实例创建后立即调用,在对象生命周期中初始化,每个对象必须正确初始化后才能正常工作。——相当于给每个创建的实例自己的属性,不许为空

(两个下划线开头的函数是声明该属性为私有,不能在类的外部被使用或访问。)

init函数的第一个参数必须是self,后面的参数则可以自由定义,和定义函数没有任何区别。

  • self总是指向调用时的类的实例。它作为类 class 的成员,使得成员间能互相调用

  • 具体构建神经网络的步骤:创建矩阵 -> 转换为变量 -> 设置神经网络 -> 进行训练

  • 把矩阵转化为变量,方便后面进行梯度计算

  • 设置神经网络是关键部分,最简单的一种包括两个部分:__ init()__函数跟forward()函数

  • __ init()__函数:

包括四个参数,分别是:初始信息,特征数,隐藏单元数,输出单元数

包含三部分:首先是继承模块,此步骤是官方步骤。然后是定义输入层到隐藏层的函数,然后定义隐藏层到输出层的函数。

输入层跟隐藏层,隐藏层跟输出层的关系,对应数量分别是1对10,10对1

  • forward函数是神经网络的前向传播函数,此函数主要会利用到激励函数,通过前面定义的各层之间的关系,产生新的结果,即新的预测值,此值会拿来跟最终的结果做比较,通过反向传播更新参数值。

  • 训练部分,首先要定义Loss函数和优化器;

  • 训练的过程:得到预测值 - - 利用Loss函数得到误差 - - 梯度清零 - - 反向传播 - - 梯度优化

  • 损失:可以把损失看成是权重和截距项的函数

编码神经元和搭建神经网络的代码和解析部分内容来自:(38条消息) Python 实现一个简单的神经网络(附代码)_神经网络算法代码_Dr.sky_的博客-CSDN博客

编码一个神经元

例:假设我们有一个神经元,激活函数就是S型函数,其参数如下:

w=[0,1],b=4

w=[0,1]就是以向量的形式表示w1=0,w2=1。现在,我们给这个神经元一个输入[2,3]。我们用点积来表示:

当输入是[2, 3]时,这个神经元的输出是0.999

##————编码一个神经元————##
def sigmoid(x):
    # 激活函数: f(x) = 1 / (1 + e^(-x))
    return 1 / (1 + np.exp(-x))

class Neuron:
    def __init__(self, weights, bias):
        self.weights = weights
        self.bias = bias

    def forward(self, inputs):
        # 加权输入,加入偏置,然后使用激活函数
        total = np.dot(self.weights, inputs) + self.bias #np.dot:向量点积
        return sigmoid(total)

weights = np.array([0, 1])  # w1 = 0, w2 = 1 权重向量
bias = 4  # b = 4 偏置值
n = Neuron(weights, bias)

x = np.array([2, 3])  # x1 = 2, x2 = 3
print(n.forward(x))  # 0.9990889488055994

把神经元组装成网络

##————编码一个如图的简易神经网络————##
class NeuralNetwork:
    """''
    A neural network with:
     - 2 inputs
     - a hidden layer with 2 neurons (h1, h2)
     - an output layer with 1 neuron (o1)
    Each neuron has the same weights and bias:
     - w = [0, 1]
     - b = 0
    ''"""
    def __init__(self):
        weights = np.array([0, 1])
        bias = 0

        #由上一部分神经元类而来
        self.h1 = Neuron(weights, bias)
        self.h2 = Neuron(weights, bias)
        self.o1 = Neuron(weights, bias)

    def feedforward(self, x):
        out_h1 = self.h1.forward(x)
        out_h2 = self.h2.forward(x)

        #o1的输入是h1和h2的输出
        out_o1 = self.o1.forward(np.array([out_h1, out_h2]))

        return out_o1

network = NeuralNetwork()
x = np.array([2, 3])
print(network.feedforward(x)) # 0.7216325609518421

训练神经网络

  • 训练过程——最小化其损失

  • 数据集中选择样本

  • 计算损失

  • 用更新等式更新每个权重和截距项

  • 重复第一步

二. AlexNet

Alexnet共有8层结构,前5层为卷积层,后三层为全连接层

各层作用:

代码实现

注:
python中的super方法:
用来解决多重继承时父类的查找问题,在子类中需要调用父类的方法时用到。好处就是可以避免直接使用父类的名字

AlexNet模型创建:

# 创建AlexNet模型
class Model(nn.Module):
    def __init__(self):
        super().__init__()
        # 输入通道数为3,因为图片为彩色,三通道
        # 而输出96、卷积核为11*11,步长为4,是由AlexNet模型决定的,后面的都同理
        self.net = nn.Sequential(nn.Conv2d(3, 96, kernel_size=11, stride=4, padding=1),
                                 nn.ReLU(),
                                 nn.MaxPool2d(kernel_size=3, stride=2),
                                 nn.Conv2d(96, 256, kernel_size=5, padding=2),
                                 nn.ReLU(),
                                 nn.MaxPool2d(kernel_size=3, stride=2),
                                 nn.Conv2d(256, 384, kernel_size=3, padding=1),
                                 nn.ReLU(),
                                 nn.Conv2d(384, 384, kernel_size=3, padding=1),
                                 nn.ReLU(),
                                 nn.Conv2d(384, 256, kernel_size=3, padding=1),
                                 nn.ReLU(),
                                 nn.MaxPool2d(kernel_size=3, stride=2),
                                 # 不要忘记在卷积--全连接的过程中,需要将数据拉平
                                 # 传入的x为[batch(每批的个数),x(长),x(宽),x(通道数)]
                                 # 变为[batch,x*x*x]
                                 nn.Flatten(),
                                 # 全连接的第一层,输入是6*6*256
                                 # 输出是由AlexNet模型决定的,为4096
                                 nn.Linear(256 * 5 * 5, 4096),
                                 nn.ReLU(),
                                 # AlexNet采取了DropOut进行正则,防止过拟合
                                 nn.Dropout(0.5),
                                 nn.Linear(4096, 4096),
                                 nn.ReLU(),
                                 nn.Dropout(0.5),
                                 # 最后一层,输出1000个类别(softmax层)这里改为10个
                                 nn.Linear(4096, 10))
    # forward方法
    def forward(self, X):
        return self.net(X)

加载torchvision库中的CIFAR-10数据集:

def getdata():
    # 下载以及转换cifar10
    # 因为图片大小不同,需要上采样为227*227
    # Compose()的主要作用是串联多个图片变换的操作
    transform = transforms.Compose([transforms.Resize((224, 224), interpolation=InterpolationMode.BICUBIC),
                                    # 转为tensor对象
                                    transforms.ToTensor(),
                                    transforms.Normalize(mean=[0.4914, 0.4822, 0.4465], std=[0.247, 0.2435, 0.2616])
                                    # 此为训练集上的均值与方差
                                    ])
    # 使用torchvision.datasets.CIFAR10()加载数据集
    # train=True表示数据集为训练数据集
    # train=False表示数据集为测试集
    # dowwnload=True表示下载数据集
    train_images = datasets.CIFAR10('./', train=True, download=True, transform=transform)
    test_images = datasets.CIFAR10('./', train=False, download=True, transform=transform)

    # 使用DataLoader来加载数据集
    # batch_size设置为100
    train_data = DataLoader(train_images, batch_size=100, shuffle=True, num_workers=2)
    test_data = DataLoader(test_images, batch_size=100, num_workers=2)

    return train_data, test_data

进行训练+测试:

# 参数初始化
def initial(layer):
    # isinstance用来判断一个对象是否是一个已知的类型
    if isinstance(layer, nn.Linear) or isinstance(layer, nn.Conv2d):
        # 服从正态分布
        nn.init.xavier_normal_(layer.weight.data)

if __name__ == '__main__':
    # 将模型放入GPU
    device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
    # 创建模型
    net = Model().to(device)
    net.apply(initial)

    epochs = 20  # # 训练20次
    lr = 0.01  # 学习率
    # 定义损失函数
    # 采取分类任务常用的交叉熵损失函数
    criterion = nn.CrossEntropyLoss()
    # 定义优化器,采取SGD优化器(随机梯度下降)
    optimizer = optim.SGD(net.parameters(), lr=lr, momentum=0.9, weight_decay=0.0005)

    # 这个优化器测试后准确度只有10%
    # 定义优化器,采取Adam优化器
    # optimizer = optim.Adam(net.parameters(), lr=lr)

    # 训练与测试
    # 记录每个epoch的训练、测试误差以及准确率
    train_loss, test_loss, train_acc, test_acc = [], [], [], []

    train_data, test_data = getdata()
    for i in range(epochs):
        # 训练
        net.train()
        # 临时变量
        temp_loss, temp_correct = 0, 0
        for X, y in train_data:
            X = X.to(device)
            y = y.to(device)

            # 一段丝滑小连招(。)
            y_hat = net(X)
            # 带入损失函数并产生loss
            loss = criterion(y_hat, y)
            # 梯度清零
            optimizer.zero_grad()
            # 反向传播
            loss.backward()
            # 梯度更新
            optimizer.step()

            # 计算每次loss与预测正确的个数
            label_hat = torch.argmax(y_hat, dim=1)
            temp_correct += (label_hat == y).sum()
            temp_loss += loss

        print(f'epoch:{i + 1}  train loss:{temp_loss / len(train_data):.3f}, train Aacc:{temp_correct / 50000 * 100:.2f}%',
              end='\t')
        train_loss.append((temp_loss / len(train_data)).item())  # 记录loss
        train_acc.append((temp_correct / 50000).item())  # 记录准确度

        # 测试
        # 预测正确个数
        temp_loss, temp_correct = 0, 0
        # 不启用 BatchNormalization 和 Dropout。此时pytorch会自动把BN和DropOut固定住,不会取平均,而是用训练好的值。
        # 不然的话,一旦test的batch_size过小,很容易就会因BN层导致模型performance损失较大
        net.eval()
        with torch.no_grad():
            for X, y in test_data:
                X = X.to(device)
                y = y.to(device)
                y_hat = net(X)
                loss = criterion(y_hat, y)
                label_hat = torch.argmax(y_hat, dim=1)
                temp_correct += (label_hat == y).sum()
                temp_loss += loss

            print(f'test loss:{temp_loss / len(test_data):.3f}, test acc:{temp_correct / 10000 * 100:.2f}%')
            test_loss.append((temp_loss / len(test_data)).item())  # 记录loss
            test_acc.append((temp_correct / 10000).item())  # 记录准确度

结果:

可以看到模型准确度越来越高了

遇到的问题

  • Adam优化器欠拟合:

????

  • 报错:“页面文件太小,无法完成操作"

解决方法:调整了虚拟内存可用硬盘空间大小

三.部署训练好的网络

onnx格式转换

  • pytorch模型保存:文件中保存模型结构和权重参数

import torch
torch.save(selfmodel,"save.pt")

pytorch模型加载:

import torch
torch.load("save.pt")
  • ONNX模型( Open Neural Network Exchage)

转换成这个标准格式后,就可以使用统一的 ONNX Runtime等工具进行统一部署。

pytorch模型转ONNX模型:

import torch
if __name__ == '__main__':
    # 加载模型
    torch_model = torch.load("MyAlexNet-CIFAR10.pt")
    # 输入数据
    input_shape = torch.randn(1, 3, 224, 224)
    # 将Tensor转换成CUDA
    device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
    torch_model = torch_model.to(device)
    torch_model.eval()
    # 目的ONNX文件名
    export_onnx_file = "MyAlexNet-CIFAR10.onnx"
    torch.onnx.export(torch_model, input_shape, export_onnx_file, verbose=True)

结果:

可以看到文件夹下已经有了.onnx的模型,转换成功

遇到的问题:

  • 需要把模型代码复制进去,不然跑不通:

  • 报错以下内容:

RuntimeError: Input type (torch.cuda.FloatTensor) and weight type (torch.FloatTensor) should be the same

但是代码中已经将tensor转换成了cuda:

 # 将Tensor转换成CUDA
    device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
    torch_model = torch_model.to(device)

猜想后,把模型中forward方法的net也放进gpu后解决:

  • opencv的DNN模块

  1. Googlenet模型实现图像分类

  1. SSD模型实现对象检测

  1. MobileNetSSD模型实时对象检测

  1. FCN模型图像分割

  1. CNN预测年龄和性别

  1. GOTURN模型实现对象跟踪

加载模型并测试

加载训练好的模型->加载图像并将图像准备为正确输入格式->运行模型获得输出

步骤详细解释:

  • 从磁盘加载预训练的AlexNet模型

使用 PyTorch深度学习框架训练的模型,已经转换为.onnx格式

DNN 模块还提供了从特定框架加载模型的函数,如:readNetFromONNX():使用它来加载 ONNX 模型,只需要提供 ONNX 模型文件的路径

先检查模型的版本、图的结构、节点及其输入和输出来验证ONNX图的有效性:

import onnx
import cv2

onnx_model = onnx.load("MyAlexNet-CIFAR10.onnx")
onnx.checker.check_model(onnx_model)

ok没问题就继续,下面这一句是主要的

net = cv2.dnn.readNetFromONNX("MyAlexNet-CIFAR10.onnx")  # 加载训练好的识别模型
  • 解压缩二进制文件的代码:

import pickle
# 解压缩二进制文件
def unpickle(file):
    fo = open(file, 'rb')
    dict = pickle.load(fo, encoding='latin1')
    fo.close()
    return dict
  • 将datasets的数据集转换为png格式并保存

import numpy as np
from PIL import Image
import os

testXtr = unpickle('cifar-10-batches-py/test_batch')
img = np.reshape(testXtr['data'][0], (3, 32, 32))
img = img.transpose(1, 2, 0)
img_name: str = 'image/' + str(testXtr['labels'][i]) + '_' + str(i) + '.jpg'
cv2.imwrite(img_name, img)

保存的图片:(标签是3)

  • 读取图像并为模型输入做好准备

使用 OpenCV 的imread() 函数读取图像,opencv dnn 模块提供blobFromImage() 函数以便我们已正确的格式准备图像以输入模型

# 1:缩放倍数 size:缩放到的大小 
blob = cv2.dnn.blobFromImage(img, 1, size=(224, 224))
  • 获取输出结果的标签:(这里卡了很久)

# 解析输出结果,获取检测物体的坐标、置信度等信息
out = []
out.append(net.forward())
boxes = []
confidences = []
class_ids = []
for output in outputs:
    for detection in output:
        scores = detection[5:]
        class_id = np.argmax(scores)
        confidence = scores[class_id]
        if confidence > threshold:
            center_x = int(detection[0] * width)
            center_y = int(detection[1] * height)
            w = int(detection[2] * width)
            h = int(detection[3] * height)
            x = center_x - w // 2
            y = center_y - h // 2
            boxes.append([x, y, w, h])
            confidences.append(float(confidence))
            class_ids.append(class_id)
  • 进行测试

net.setInput(blob)  # 设置模型输入
out = []
out.append(net.forward())

结果:class_id就是测试的标签,可以看到也是3

问题:

  1. net.forward()提供了Numpy ndarray作为输出,网上说可以使用它在给定的输入图像上绘制方框。但具体的返回值还不清楚))

  1. 加入循环进行多个图片的测试时,只能打印出第一个测试图片的标签,可能还是因为问题(1)中说的net.forward()返回值不清楚

PS:感谢学姐的小机器人))T T

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值