深度学习基础

文章目录

  • 1. 数学基础
    • 1.1 标量和向量
    • 1.2 向量运算
    • 1.3 矩阵
    • 1.4 张量
    • 1.5 导数
  • 2. numpy常用操作
  • 3. 梯度下降算法
  • 4. 反向传播
    • 4.1 完整的反向传播过程
    • 4.2 代码演示
  • 5. 网络结构 -- 全连接层
  • 6. 激活函数
    • 6.1 激活函数-Sigmoid
    • 6.2 激活函数-tanh
    • 6.3 激活函数-Relu
    • 6.4 激活函数-Softmax
  • 7. 损失函数
    • 7.1 损失函数-均方差
    • 7.2 损失函数-交叉熵
    • 7.3 损失函数-其他
  • 8. 优化器
  • 9. Pytorch

1. 数学基础

1.1 标量和向量

标量 Scalar
一个标量就是一个单独的数

向量 Vector
一个向量是一列数
可以把向量看做空间中的点,每个元素是不同坐标轴上的坐标
向量中有几个数,就叫几维向量
如4维向量:[1,2,3,4]

1.2 向量运算

向量加和: A +B = B + A 需要维度相同
[1,2] + [3,4] = [4, 6]

向量内积: A * B = B * A 需要维度相同
[1,2] * [3, 4] = 1 * 3 + 2* 4 = 11

1.3 矩阵

矩阵 matrix
是一个二维数组。矩阵中的每一个值是一个标量,可以通过行号和列号进行索引
矩阵加法 需要维度相同

矩阵乘法
不满足交换律 A * B != B* A
当左矩阵A的列数等于右矩阵B的行数时,A与B可以相乘
M x N 矩阵乘以 N x P矩阵得到M x P维度矩阵
深度学习基础_第1张图片

符合分配律
A*(B+C) = A*B + A * C

符合结合律
A * (B * C) = (A * B) * C

另一种矩阵乘法 – 矩阵点乘
两矩阵必须形状一致

1.4 张量

张量 tensor
将三个2 x 2的矩阵排列在一起,就可以称为一个 3 x 2 x 2的张量
将4个3 x 2 x 2的张量排列在一起,就可以成为一个 4 x 3 x 2 x 2维度的张量。
张量是神经网络的训练中最为常见的数据形式。
所有的输入,输出,中间结果,几乎都是以张量的形式存在。

1.5 导数

导数表示函数变化的方向
深度学习基础_第2张图片
深度学习基础_第3张图片

常见导数
深度学习基础_第4张图片
深度学习基础_第5张图片

求导法则
加(减)法则:[f(x)+g(x)]‘=f(x)’+g(x)’

乘法法则:[f(x)*g(x)]‘=f(x)’*g(x)+g(x)'*f(x)

除法法则:[f(x)/g(x)]‘=[f(x)’*g(x)-g(x)'*f(x)]/g(x)2

链式法则:若h(x)=f(g(x)),则h’(x)=f’(g(x))g’(x)

2. numpy常用操作

NumPy 是 Python 中的一个开源库,提供大量的数学函数和多维数组对象,以及各种派生对象(如掩码数组和矩阵),这些工具可用于各种数学和逻辑操作。它是大多数数据科学和科学计算Python工具的基础。
深度学习基础_第6张图片
深度学习基础_第7张图片
深度学习基础_第8张图片
深度学习基础_第9张图片
深度学习基础_第10张图片
深度学习基础_第11张图片

代码示例

#coding:utf8

import torch
import numpy as np



#numpy基本操作
x = np.array([[1,2,3],
              [4,5,6]])
#
print(x.ndim)
print(x.shape)
print(x.size)
print(np.sum(x))
print(np.sum(x, axis=0))
print(np.sum(x, axis=1))
print(np.reshape(x, (3,2)))
print(np.sqrt(x))
print(np.exp(x))
print(x.transpose())
print(x.flatten())

#
# print(np.zeros((3,4,5)))
# print(np.random.rand(3,4,5))
#
# x = np.random.rand(3,4,5)
x = torch.FloatTensor(x)
print(x.shape)
print(torch.exp(x))
print(torch.sum(x, dim=0))
print(torch.sum(x, dim=1))
print(x.transpose(1, 0))
print(x.flatten())


3. 梯度下降算法

梯度下降算法是一个用于寻找函数最小值的迭代优化算法。在机器学习和深度学习中,通常使用梯度下降来优化损失函数,从而训练模型的参数。

梯度下降的核心思想是使用函数的梯度(或近似梯度)来确定在函数值减小的方向上迭代更新参数。换句话说,如果你站在山坡上并想找到最短的路径下山,那么在每一步,你应该朝着最陡峭的下坡方向走。

算法步骤:

  1. 初始化参数(通常是随机的)。
  2. 计算损失函数的梯度。
  3. 更新参数,使其沿着梯度的负方向移动:
    θ=θ−α×∇θ ,其中 α 是学习率。
  4. 重复步骤 2 和 3,直到满足停止准则(例如,梯度接近于零,达到最大迭代次数,或损失变化很小)。

深度学习基础_第12张图片

学习率 α 决定了每次迭代时参数更新的步长。太大的学习率可能会使算法在最小值附近震荡,而太小的学习率可能会导致算法收敛得太慢。很多高级的优化算法(如 Adam、RMSprop 等)都是为了在训练过程中自动调整学习率。

权重更新方式

Gradient descent
所有样本一起计算梯度(累加)

Stochastic gradient descent
每次使用一个样本计算梯度

Mini-batch gradient descent
每次使用n个样本计算梯度(累加)

找极小值问题
深度学习基础_第13张图片

函数f(x)的值受x影响

目标:找到合适的x值,使得f(x)最小

方法:

  1. 任取一点x0,计算在这一点的导数值f(x0)
  2. 根据导数的正负,决定x0应当调大还是调小
  3. 迭代进行1,2直到x不在变化(或变化极小)

深度学习基础_第14张图片
原函数为 y = x2 导函数为 y = 2*x
在x = -1这个点,导数值为 -2
该点导数为负数,说明在这一点,如果x增大,y会减小
所以f(x)最小值的点应当在-1的右侧(大于-1)

深度学习基础_第15张图片
原函数为 y = x2 导函数为 y = 2*x
在x = 1这个点,导数值为 2
该点导数为正数,说明在这一点,如果x增大,y会增大
所以f(x)最小值的点应当在-1的左侧(小于1)

梯度

  • 梯度:可以理解为多元函数的导数,意义与导数基本一致

原函数:y = 3x2
导函数: y = 6x
在x=1处的导数值:6
深度学习基础_第16张图片

原函数:y = 3x12 + 4x22 + 5x32
导函数:y = {6x1 , 8x2 , 10x3}
在[1,1,1]处的梯度是[6,8,10]
梯度是个向量
深度学习基础_第17张图片

4. 反向传播

4.1 完整的反向传播过程

  1. 根据输入x和模型当前权重,计算预测值y’
  2. 根据y’和y使用loss函数计算loss
  3. 根据loss计算模型权重的梯度
  4. 使用梯度和学习率,根据优化器调整模型权重

4.2 代码演示

# coding:utf8

import torch
import torch.nn as nn
import numpy as np
import copy

"""
基于pytorch的网络编写
手动实现梯度计算和反向传播
加入激活函数
"""


class TorchModel(nn.Module):
    def __init__(self, hidden_size):
        super(TorchModel, self).__init__()
        self.layer = nn.Linear(hidden_size, hidden_size, bias=False)
        self.activation = torch.sigmoid
        self.loss = nn.functional.mse_loss  # loss采用均方差损失

    # 当输入真实标签,返回loss值;无真实标签,返回预测值
    def forward(self, x, y=None):
        y_pred = self.layer(x)
        y_pred = self.activation(y_pred)
        if y is not None:
            return self.loss(y_pred, y)
        else:
            return y_pred


# 自定义模型,接受一个参数矩阵作为入参
class DiyModel:
    def __init__(self, weight):
        self.weight = weight

    def forward(self, x, y=None):
        y_pred = np.dot(self.weight, x)
        y_pred = self.diy_sigmoid(y_pred)
        if y is not None:
            return self.diy_mse_loss(y_pred, y)
        else:
            return y_pred

    # sigmoid
    def diy_sigmoid(self, x):
        return 1 / (1 + np.exp(-x))

    # 手动实现mse,均方差loss
    def diy_mse_loss(self, y_pred, y_true):
        return np.sum(np.square(y_pred - y_true)) / len(y_pred)

    # 手动实现梯度计算
    def calculate_grad(self, y_pred, y_true, x):
        # 前向过程
        # wx = np.dot(self.weight, x)
        # sigmoid_wx = self.diy_sigmoid(wx)
        # loss = self.diy_mse_loss(sigmoid_wx, y_true)
        # 反向过程
        # 均方差函数 (y_pred - y_true) ^ 2 / n 的导数 = 2 * (y_pred - y_true) / n
        grad_loss_sigmoid_wx = 2/len(x) * (y_pred - y_true)
        # sigmoid函数 y = 1/(1+e^(-x)) 的导数 = y * (1 - y)
        grad_sigmoid_wx_wx = y_pred * (1 - y_pred)
        # wx对w求导 = x
        grad_wx_w = x
        # 导数链式相乘
        grad = grad_loss_sigmoid_wx * grad_sigmoid_wx_wx
        grad = np.dot(grad.reshape(len(x), 1), grad_wx_w.reshape(1, len(x)))
        return grad

# 梯度更新


def diy_sgd(grad, weight, learning_rate):
    return weight - learning_rate * grad

# adam梯度更新


def diy_adam(grad, weight):
    # 参数应当放在外面,此处为保持后方代码整洁简单实现一步
    alpha = 1e-3  # 学习率
    beta1 = 0.9  # 超参数
    beta2 = 0.999  # 超参数
    eps = 1e-8  # 超参数
    t = 0  # 初始化
    mt = 0  # 初始化
    vt = 0  # 初始化
    # 开始计算
    t = t + 1
    gt = grad
    mt = beta1 * mt + (1 - beta1) * gt
    vt = beta2 * vt + (1 - beta2) * gt ** 2
    mth = mt / (1 - beta1 ** t)
    vth = vt / (1 - beta2 ** t)
    weight = weight - (alpha / (np.sqrt(vth) + eps)) * mth
    return weight


x = np.array([1, 2, 3, 4])  # 输入
y = np.array([0.1, -0.1, 0.01, -0.01])  # 预期输出

# torch实验
torch_model = TorchModel(len(x))
torch_model_w = torch_model.state_dict()["layer.weight"]
print(torch_model_w, "初始化权重")
numpy_model_w = copy.deepcopy(torch_model_w.numpy())
# numpy array -> torch tensor, unsqueeze的目的是增加一个batchsize维度
torch_x = torch.from_numpy(x).float().unsqueeze(0)
torch_y = torch.from_numpy(y).float().unsqueeze(0)
# torch的前向计算过程,得到loss
torch_loss = torch_model(torch_x, torch_y)
print("torch模型计算loss:", torch_loss)
# #手动实现loss计算
diy_model = DiyModel(numpy_model_w)
diy_loss = diy_model.forward(x, y)
print("diy模型计算loss:", diy_loss)

# #设定优化器
learning_rate = 0.1
optimizer = torch.optim.SGD(torch_model.parameters(), lr=learning_rate)
# optimizer = torch.optim.Adam(torch_model.parameters())
optimizer.zero_grad()
#
# #pytorch的反向传播操作
torch_loss.backward()
print(torch_model.layer.weight.grad, "torch 计算梯度")  # 查看某层权重的梯度

# #手动实现反向传播
grad = diy_model.calculate_grad(diy_model.forward(x), y, x)
print(grad, "diy 计算梯度")
#
# #torch梯度更新
# optimizer.step()
# #查看更新后权重
# update_torch_model_w = torch_model.state_dict()["layer.weight"]
# print(update_torch_model_w, "torch更新后权重")
#
# #手动梯度更新
# diy_update_w = diy_sgd(grad, numpy_model_w, learning_rate)
# diy_update_w = diy_adam(grad, numpy_model_w)
# print(diy_update_w, "diy更新权重")

5. 网络结构 – 全连接层

全连接层又称线性层
计算公式:y = w * x + b
W和b是参与训练的参数
W的维度决定了隐含层输出的维度,一般称为隐单元个数(hidden size)
举例:
输入:x (维度1 x 3)
隐含层1:w(维度3 x 5)
隐含层2: w(维度5 x 2)
深度学习基础_第18张图片

代码示例

#coding:utf8

import torch
import torch.nn as nn
import numpy as np

"""
numpy手动实现模拟一个线性层
"""

#搭建一个2层的神经网络模型
#每层都是线性层
class TorchModel(nn.Module):
    def __init__(self, input_size, hidden_size1, hidden_size2):
        super(TorchModel, self).__init__()
        self.layer1 = nn.Linear(input_size, hidden_size1)
        self.layer2 = nn.Linear(hidden_size1, hidden_size2)

    def forward(self, x):
        hidden = self.layer1(x)   #shape: (batch_size, input_size) -> (batch_size, hidden_size1)
        y_pred = self.layer2(hidden) #shape: (batch_size, hidden_size1) -> (batch_size, hidden_size2)
        return y_pred

#自定义模型
class DiyModel:
    def __init__(self, w1, b1, w2, b2):
        self.w1 = w1
        self.b1 = b1
        self.w2 = w2
        self.b2 = b2

    def forward(self, x):
        hidden = np.dot(x, self.w1.T) + self.b1
        y_pred = np.dot(hidden, self.w2.T) + self.b2
        return y_pred



#随便准备一个网络输入
x = np.array([34.1, 0.3, 1.2])
#建立torch模型
torch_model = TorchModel(len(x), 5, 2)
print(torch_model.state_dict())
print("-----------")
#打印模型权重,权重为随机初始化
torch_model_w1 = torch_model.state_dict()["layer1.weight"].numpy()
torch_model_b1 = torch_model.state_dict()["layer1.bias"].numpy()
torch_model_w2 = torch_model.state_dict()["layer2.weight"].numpy()
torch_model_b2 = torch_model.state_dict()["layer2.bias"].numpy()
print(torch_model_w1, "torch w1 权重")
print(torch_model_b1, "torch b1 权重")
print("-----------")
print(torch_model_w2, "torch w2 权重")
print(torch_model_b2, "torch b2 权重")
print("-----------")
#使用torch模型做预测
torch_x = torch.FloatTensor([x])
y_pred = torch_model.forward(torch_x)
print("torch模型预测结果:", y_pred)
#把torch模型权重拿过来自己实现计算过程
diy_model = DiyModel(torch_model_w1, torch_model_b1, torch_model_w2, torch_model_b2)
#用自己的模型来预测
y_pred_diy = diy_model.forward(np.array([x]))
print("diy模型预测结果:", y_pred_diy)

6. 激活函数

模型添加非线性因素,使模型具有拟合非线性函数的能力

6.1 激活函数-Sigmoid

一种非线性映射,将任意输入映射到0-1之间
深度学习基础_第19张图片

深度学习基础_第20张图片
深度学习基础_第21张图片

缺点:

  1. 计算耗时,包含指数运算
  2. 非0均值,会导致收敛慢
  3. 易造成梯度消失

代码示例

import numpy as np

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

# 示例
z = np.array([-1.0, 0.0, 1.0])
print(sigmoid(z))

6.2 激活函数-tanh

以0为均值,解决了sigmoid的一定缺点,但是依然存在梯度消失问题计算同样非常耗时
深度学习基础_第22张图片

在这里插入图片描述

深度学习基础_第23张图片

import numpy as np

def tanh(z):
    return np.tanh(z)

# 示例
z = np.array([-1.0, 0.0, 1.0])
print(tanh(z))

6.3 激活函数-Relu

在正区间不易发生梯度消失
计算速度非常快
一定程度上降低过拟合的风险
深度学习基础_第24张图片

深度学习基础_第25张图片

import numpy as np

def relu(z):
    return np.maximum(0, z)

# 示例
z = np.array([-1.0, 0.0, 1.0])
print(relu(z))

6.4 激活函数-Softmax

深度学习基础_第26张图片
深度学习基础_第27张图片

代码示例

#coding:utf8
import torch
import numpy

'''
softmax的计算
'''

def softmax(x):
    res = []
    for i in x:
        res.append(numpy.exp(i))
    res = [r / sum(res) for r in res]
    return res

#e的1次方
print(numpy.exp(1))

x = [1,2,3,4]
#torch实现的softmax
print(torch.softmax(torch.Tensor(x), 0))
#自己实现的softmax
print(softmax(x))

7. 损失函数

损失函数(也称为代价函数或误差函数)是一个核心组件于许多机器学习、统计学和优化任务中。它用于描述模型预测值与真实值之间的差异。通过最小化损失函数,我们可以找到模型的最佳参数,使模型的预测更加接近实际值。

7.1 损失函数-均方差

MSE mean square error
对均方差在做开根号,可以得到根方差
在这里插入图片描述
深度学习基础_第28张图片

import numpy as np

def mean_squared_error(y_true, y_pred):
    return ((y_true - y_pred) ** 2).mean()

# 示例
y_true = np.array([2.0, 4.0, 6.0])
y_pred = np.array([2.5, 3.5, 5.5])
print(mean_squared_error(y_true, y_pred))

7.2 损失函数-交叉熵

Cross Entropy
常用于分类任务
分类任务中,网络输出经常是所有类别上的概率分布
公式:
深度学习基础_第29张图片

假设一个三分类任务,某样本的正确标签是第一类,则p = [1, 0, 0], 模型预测值假设为[0.5, 0.4, 0.1], 则交叉熵计算如下:
在这里插入图片描述

代码示例

import torch
import torch.nn as nn
import numpy as np

'''
手动实现交叉熵的计算
'''

#使用torch计算交叉熵
ce_loss = nn.CrossEntropyLoss()
#假设有3个样本,每个都在做3分类
pred = torch.FloatTensor([[0.3, 0.1, 0.3],
                          [0.9, 0.2, 0.9],
                          [0.5, 0.4, 0.2]])
#正确的类别分别为1,2,0
target = torch.LongTensor([1, 2, 0])
loss = ce_loss(pred, target)
print(loss, "torch输出交叉熵")


#实现softmax函数
def softmax(matrix):
    return np.exp(matrix) / np.sum(np.exp(matrix), axis=1, keepdims=True)

#验证softmax函数
# print(torch.softmax(pred, dim=1))
# print(softmax(pred.numpy()))


#将输入转化为onehot矩阵
def to_one_hot(target, shape):
    one_hot_target = np.zeros(shape)
    for i, t in enumerate(target):
        one_hot_target[i][t] = 1
    return one_hot_target

#手动实现交叉熵
def cross_entropy(pred, target):
    batch_size, class_num = pred.shape
    pred = softmax(pred)
    target = to_one_hot(target, pred.shape)
    entropy = - np.sum(target * np.log(pred), axis=1)
    return sum(entropy) / batch_size

print(cross_entropy(pred.numpy(), target.numpy()), "手动实现交叉熵")

7.3 损失函数-其他

指数损失
深度学习基础_第30张图片

对数损失
在这里插入图片描述

0/1损失
深度学习基础_第31张图片

Hinge损失(二分类)
深度学习基础_第32张图片

8. 优化器

优化器-Adam

  1. 实现简单,计算高效,对内存需求少
  2. 超参数具有很好的解释性,且通常无需调整或仅需很少的微调
  3. 更新的步长能够被限制在大致的范围内(初始学习率)
  4. 能够表现出自动调整学习率
  5. 很适合应用于大规模的数据及参数的场景
  6. 适用于不稳定目标函数
  7. 适用于梯度稀疏或梯度存在很大噪声的问题

9. Pytorch

代码示例

# coding:utf8

import torch
import torch.nn as nn
import numpy as np
import random
import json
import matplotlib.pyplot as plt

"""

基于pytorch框架编写模型训练
实现一个自行构造的找规律(机器学习)任务
规律:x是一个5维向量,如果第1个数>第5个数,则为正样本,反之为负样本

"""


class TorchModel(nn.Module):
    def __init__(self, input_size):
        super(TorchModel, self).__init__()
        self.linear = nn.Linear(input_size, 1)  # 线性层
        self.activation = torch.sigmoid  # sigmoid归一化函数
        self.loss = nn.functional.mse_loss  # loss函数采用均方差损失

    # 当输入真实标签,返回loss值;无真实标签,返回预测值
    def forward(self, x, y=None):
        x = self.linear(x)  # (batch_size, input_size) -> (batch_size, 1)
        y_pred = self.activation(x)  # (batch_size, 1) -> (batch_size, 1)
        if y is not None:
            return self.loss(y_pred, y)  # 预测值和真实值计算损失
        else:
            return y_pred  # 输出预测结果


# 生成一个样本, 样本的生成方法,代表了我们要学习的规律
# 随机生成一个5维向量,如果第一个值大于第五个值,认为是正样本,反之为负样本
def build_sample():
    x = np.random.random(5)
    if x[0] > x[4]:
        return x, 1
    else:
        return x, 0


# 随机生成一批样本
# 正负样本均匀生成
def build_dataset(total_sample_num):
    X = []
    Y = []
    for i in range(total_sample_num):
        x, y = build_sample()
        X.append(x)
        Y.append([y])
    return torch.FloatTensor(X), torch.FloatTensor(Y)


# 测试代码
# 用来测试每轮模型的准确率
def evaluate(model):
    model.eval()
    test_sample_num = 100
    x, y = build_dataset(test_sample_num)
    print("本次预测集中共有%d个正样本,%d个负样本" % (sum(y), test_sample_num - sum(y)))
    correct, wrong = 0, 0
    with torch.no_grad():
        y_pred = model(x)  # 模型预测
        for y_p, y_t in zip(y_pred, y):  # 与真实标签进行对比
            if float(y_p) < 0.5 and int(y_t) == 0:
                correct += 1  # 负样本判断正确
            elif float(y_p) >= 0.5 and int(y_t) == 1:
                correct += 1  # 正样本判断正确
            else:
                wrong += 1
    print("正确预测个数:%d, 正确率:%f" % (correct, correct / (correct + wrong)))
    return correct / (correct + wrong)


def main():
    # 配置参数
    epoch_num = 10  # 训练轮数
    batch_size = 20  # 每次训练样本个数
    train_sample = 5000  # 每轮训练总共训练的样本总数
    input_size = 5  # 输入向量维度
    learning_rate = 0.001  # 学习率
    # 建立模型
    model = TorchModel(input_size)
    # 选择优化器
    optim = torch.optim.Adam(model.parameters(), lr=learning_rate)
    log = []
    # 创建训练集,正常任务是读取训练集
    train_x, train_y = build_dataset(train_sample)
    # 训练过程
    for epoch in range(epoch_num):
        model.train()
        watch_loss = []
        for batch_index in range(train_sample // batch_size):
            x = train_x[batch_index * batch_size : (batch_index + 1) * batch_size]
            y = train_y[batch_index * batch_size : (batch_index + 1) * batch_size]
            optim.zero_grad()  # 梯度归零
            loss = model(x, y)  # 计算loss
            loss.backward()  # 计算梯度
            optim.step()  # 更新权重
            watch_loss.append(loss.item())
        print("=========\n第%d轮平均loss:%f" % (epoch + 1, np.mean(watch_loss)))
        acc = evaluate(model)  # 测试本轮模型结果
        log.append([acc, float(np.mean(watch_loss))])
    # 保存模型
    torch.save(model.state_dict(), "model.pth")
    # 画图
    print(log)
    plt.plot(range(len(log)), [l[0] for l in log], label="acc")  # 画acc曲线
    plt.plot(range(len(log)), [l[1] for l in log], label="loss")  # 画loss曲线
    plt.legend()
    plt.show()
    return


# 使用训练好的模型做预测
def predict(model_path, input_vec):
    input_size = 5
    model = TorchModel(input_size)
    model.load_state_dict(torch.load(model_path))  # 加载训练好的权重
    # print(model.state_dict())

    model.eval()  # 测试模式
    with torch.no_grad():  # 不计算梯度
        result = model.forward(torch.FloatTensor(input_vec))  # 模型预测
    for vec, res in zip(input_vec, result):
        print("输入:%s, 预测类别:%d, 概率值:%f" % (vec, round(float(res)), res))  # 打印结果


if __name__ == "__main__":
    main()
    test_vec = [[0.47889086,0.15229675,0.31082123,0.03504317,0.18920843],
                [0.94963533,0.5524256,0.95758807,0.95520434,0.84890681],
                [0.78797868,0.67482528,0.13625847,0.34675372,0.99871392],
                [0.1349776,0.59416669,0.92579291,0.41567412,0.7358894]]
    predict("model.pth", test_vec)

你可能感兴趣的:(深度学习,深度学习,人工智能)