• 【自然语言处理】深度学习基础


    01. 引入

    神经网络处理文本:字符串分类 - 判断字符串中是否出现了指定字符

    例:指定字符a样本:

    abcd 正样本

    bcde 负样本

    当前输入:字符串abcd

    预期输出:概率值 正样本=1,负样本=0,以0.5为分界

    X = “abcd” Y = 1

    X =“bcde” Y = 0

    建模目标:找到一个映射f(x),使得f(“abcd”) = 1, f(“bcde”) = 0

    字符数值化直观方式,a -> 1, b -> 2, c -> 3 …. z -> 26 不太合理,因为数值化之后每个字母之间就有一定的等式关系

    每个字符转化成同维度向量a - > [0.32618175 0.20962898 0.43550067 0.07120884 0.58215387]b - > [0.21841921 0.97431001 0.43676452 0.77925024 0.7307891 ]z -> [0.72847746 0.72803551 0.43888069 0.09266955 0.65148562]

    字符数值化“abcd” - > 4 * 5 的矩阵 [[0.32618175 0.20962898 0.43550067 0.07120884 0.58215387] [0.21841921 0.97431001 0.43676452 0.77925024 0.7307891 ] [0.95035602 0.45280039 0.06675379 0.72238734 0.02466642] [0.86751814 0.97157839 0.0127658 0.98910503 0.92606296]]

    矩阵形状 = 文本长度 * 向量长度

    矩阵转化为向量求平均(池化过程)

    [[0.32618175 0.20962898 0.43550067 0.07120884 0.58215387] [0.21841921 0.97431001 0.43676452 0.77925024 0.7307891 ] [0.95035602 0.45280039 0.06675379 0.72238734 0.02466642] [0.86751814 0.97157839 0.0127658 0.98910503 0.92606296]]- >[0.59061878 0.65207944 0.2379462 0.64048786 0.56591809](相加除以四)

    由4 * 5 矩阵 -> 1* 5 向量 形状 = 1*向量长度

    向量到数值采取最简单的线性公式 y = w * x + b

    w 维度为1*向量维度,b为实数例:

    w = [1, 1], b = -1, x = [1,2]

    [1,1] * [1 2] - 1 = 1*1 + 1*2 - 1 = 2

    数值归一化

    sigma函数
    σ ( x ) = 1 1 + e − x σ(x)=\frac{1}{1+e^{-x}} σ(x)=1+ex1
    x = 3 σ(x) = 0.9526

    在这里插入图片描述

    整体映射

    “abcd” ----每个字符转化成向量Embedding层)----> 4 * 5矩阵

    4 * 5矩阵 ----向量求平均(池化层)----> 1 * 5向量

    1 * 5向量 ----w*x + b线性公式 —> 实数

    实数 ----sigmoid归一化函数—> 0-1之间实数

    加粗部分需要通过训练优化

    02. 神经网络

    2.1 Embedding层

    Embedding矩阵是可训练的参数,一般会在模型构建时随机初始化,也可以使用预训练的词向量来做初始化,此时也可以选择不训练Embedding层中的参数

    输入的整数序列可以有重复,但取值不能超过Embedding矩阵的列数

    核心价值:将离散值转化为向量

    #coding:utf8
    import torch
    import torch.nn as nn
    
    '''
    embedding层的处理
    '''
    
    num_embeddings = 6  #通常对于nlp任务,此参数为字符集字符总数
    embedding_dim = 3   #每个字符向量化后的向量维度
    embedding_layer = nn.Embedding(num_embeddings, embedding_dim)
    print(embedding_layer.weight, "随机初始化权重")
    
    #构造输入
    x = torch.LongTensor([[1,2,3],[2,2,0]])
    embedding_out = embedding_layer(x)# 传入构造的embedding_layer层
    print(embedding_out)
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    运行结果:

    Parameter containing:
    tensor([[-1.6804, -1.7307, -1.2290],
            [-0.3990,  0.4878,  2.5067],
            [-1.5682, -2.1808,  0.2217],
            [-1.1663, -1.5519, -0.6108],
            [-0.0392, -0.1552,  2.2150],
            [ 0.5951,  0.6064, -0.2041]], requires_grad=True) 随机初始化权重
    tensor([[[-0.3990,  0.4878,  2.5067],
             [-1.5682, -2.1808,  0.2217],
             [-1.1663, -1.5519, -0.6108]],
    
            [[-1.5682, -2.1808,  0.2217],
             [-1.5682, -2.1808,  0.2217],
             [-1.6804, -1.7307, -1.2290]]], grad_fn=)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.2 网络结构

    2.2.1 网络结构-DNN

    全连接层又称线性层,计算公式:y = w * x + b

    W和b是参与训练的参数,W的维度决定了隐含层输出的维度,一般称为隐单元个数(hidden size)

    举例:输入:x (维度1 x 3)隐含层1:w(维度3 x 5)隐含层2: w(维度5 x 2)DNN-numpy手动实现模拟一个线性层

    #coding:utf8
    
    import torch
    import torch.nn as nn
    import numpy as np
    
    """
    numpy手动实现模拟一个线性层
    """
    
    #搭建一个2层的神经网络模型
    #每层都是线性层
    # 定义类,然后继承nn.Module类
    class TorchModel(nn.Module):
        # 定义组件
        def __init__(self, input_size, hidden_size1, hidden_size2):
            super(TorchModel, self).__init__()
            # 第一层input_size维映射到hidden_size1维
            self.layer1 = nn.Linear(input_size, hidden_size1)
            # 第二层hidden_size1维映射到hidden_size2维···以此类推
            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
    
    #随便准备一个网络输入
    x = np.array([1, 0, 2])
    print(x)
    #建立torch模型
    torch_model = TorchModel(len(x), 5, 2)
    print(torch_model.state_dict())# 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(np.array([x]))
    y_pred = torch_model.forward(torch_x)
    print("torch模型预测结果:", y_pred)
    #把torch模型权重拿过来自己实现计算过程
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    自定义模型,就是不使用接口将以上模型写出来

    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 # dot函数计算的是两个数组的点积
            y_pred = np.dot(hidden, self.w2.T) + self.b2
            return y_pred
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.2.2 网络结构-RNN

    循环神经网络(recurrent neural network)

    主要思想:将整个序列划分成多个时间步,将每一个时间步的信息依次输入模型,同时将模型输出的结果传给下一个时间步。

    公式:(有时b可省略)
    h ( t ) = t a n h ( b + W h ( t − 1 ) + U x ( t ) ) h^{(t)}=tanh(b+Wh^{(t-1)}+Ux^{(t)}) h(t)=tanh(b+Wh(t1)+Ux(t))
    模型:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

    注意:一般都是先RNN再Polling。因为如果先pooling的话RNN时就会缺少部分数据,这不是RNN的初衷

    使用封装模型

    #coding:utf8
    
    import torch
    import torch.nn as nn
    import numpy as np
    
    
    """
    使用pytorch实现RNN
    """
    # 搭建模型
    class TorchRNN(nn.Module):
        def __init__(self, input_size, hidden_size):
            super(TorchRNN, self).__init__()
            self.layer = nn.RNN(input_size, hidden_size, bias=False, batch_first=True)
    
        def forward(self, x):
            return self.layer(x)
    
    
    x = np.array([[1, 2, 3],
                  [3, 4, 5],
                  [5, 6, 7]])  #网络输入
    
    #torch实验
    hidden_size = 4
    torch_model = TorchRNN(3, hidden_size)
    print(torch_model.state_dict())
    w_ih = torch_model.state_dict()["layer.weight_ih_l0"]
    w_hh = torch_model.state_dict()["layer.weight_hh_l0"]
    
    torch_x = torch.FloatTensor(np.array([x]))
    output, h = torch_model.forward(torch_x)
    print(h)
    print(output.detach().numpy(), "torch模型预测结果")
    print(h.detach().numpy(), "torch模型预测隐含层结果")
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    手动建模型

    #自定义RNN模型,就是用来做个对比,以后的项目中不需要这一步
    #w_ih就是公式中的u,W_hh就是公式中的w
    class DiyModel:
        def __init__(self, w_ih, w_hh, hidden_size):
            self.w_ih = w_ih
            self.w_hh = w_hh
            self.hidden_size = hidden_size
    
        def forward(self, x):
            ht = np.zeros((self.hidden_size))
            output = []
            #公式的复现
            for xt in x:
                ux = np.dot(self.w_ih, xt)
                wh = np.dot(self.w_hh, ht)
                ht_next = np.tanh(ux + wh)
                output.append(ht_next)
                ht = ht_next
            return np.array(output), ht
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    注意:

    ①RNN不建议堆叠得很深,大概三层就够了,因为效率不是很高

    ②RNN可以认为是把每个向量的第一维取出,依次取出每一维

    2.2.3 网络结构-CNN

    以卷积操作为基础的网络结构,每个卷积核可以看成一个特征提取器

    在这里插入图片描述

    CNN模型

    #coding:utf8
    
    import torch
    import torch.nn as nn
    import numpy as np
    
    
    """
    手动实现简单的神经网络
    使用pytorch实现CNN
    手动实现CNN
    对比
    """
    # in_channel:加入是一张灰度图,就是一个单通道的图,值为1
    class TorchCNN(nn.Module):
        def __init__(self, in_channel, out_channel, kernel):
            super(TorchCNN, self).__init__()
            self.layer = nn.Conv2d(in_channel, out_channel, kernel, bias=False)
    
        def forward(self, x):
            return self.layer(x)
    
    
    
    x = np.array([[0.1, 0.2, 0.3, 0.4],
                  [-3, -4, -5, -6],
                  [5.1, 6.2, 7.3, 8.4],
                  [-0.7, -0.8, -0.9, -1]])  #网络输入
    
    #torch实验
    in_channel = 1
    out_channel = 3 #三个卷积核
    kernel_size = 2 #2*2的卷积核
    torch_model = TorchCNN(in_channel, out_channel, kernel_size)
    print(torch_model.state_dict())#打印权重
    torch_w = torch_model.state_dict()["layer.weight"]
    print(torch_w.numpy().shape)
    torch_x = torch.FloatTensor([[x]])
    output = torch_model.forward(torch_x)
    output = output.detach().numpy()
    print(output, output.shape, "torch模型预测结果\n")
    print("---------------")
    diy_model = DiyModel(x.shape[0], x.shape[1], torch_w, kernel_size)
    output = diy_model.forward(x)
    print(output, "diy模型预测结果")
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    自定义CNN模型

    
    class DiyModel:
        def __init__(self, input_height, input_width, weights, kernel_size):
            self.height = input_height
            self.width = input_width
            self.weights = weights#对应的权重
            self.kernel_size = kernel_size
    
        def forward(self, x):
            output = []
            for kernel_weight in self.weights:
                kernel_weight = kernel_weight.squeeze().numpy() #shape : 2x2
                kernel_output = np.zeros((self.height - kernel_size + 1, self.width - kernel_size + 1))
                for i in range(self.height - kernel_size + 1):
                    for j in range(self.width - kernel_size + 1):
                        window = x[i:i+kernel_size, j:j+kernel_size]
                        kernel_output[i, j] = np.sum(kernel_weight * window) # np.dot(a, b) != a * b
                output.append(kernel_output)
            return np.array(output)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.2.4 总结

    网络结构的理解:不同的网络结构区别就是不同的函数,就会出现不同的参数

    以DNN为例解释代码模型结构

    # 定义类,然后继承nn.Module类
    class TorchModel(nn.Module):
        # 定义组件
        def __init__(self, input_size, hidden_size1, hidden_size2):
            super(TorchModel, self).__init__()
            # 第一层input_size维映射到hidden_size1维
            self.layer1 = nn.Linear(input_size, hidden_size1)
            # 第二层hidden_size1维映射到hidden_size2维···以此类推
            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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.3 池化层

    降维:降低了后续网络层的输入维度,缩减模型大小,提高计算速度

    防止过拟合:提高了Feature Map 的鲁棒性,防止过拟合在这里插入图片描述

    pooling层

    #coding:utf8
    import torch
    import torch.nn as nn
    
    '''
    pooling层的处理
    '''
    
    #pooling操作默认对于输入张量的最后一维进行
    #入参5,代表把五维池化为一维
    layer = nn.AvgPool1d(5)# 取平均;MaxPool1d()取最大值
    #随机生成一个维度为3x4x5的张量
    #可以想象成3条,文本长度为4,向量长度为5的样本
    x = torch.rand([3, 4, 5])
    print(x)
    print(x.shape)
    #经过pooling层
    y = layer(x)
    print(y)
    print(y.shape)
    #squeeze方法去掉值为1的维度
    y = y.squeeze()
    print(y)
    print(y.shape)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    运行结果:

    tensor([[[0.8307, 0.1603, 0.7290, 0.4198, 0.1470],
             [0.6383, 0.1021, 0.2992, 0.7656, 0.5260],
             [0.6156, 0.3681, 0.9710, 0.4929, 0.9303],
             [0.2452, 0.4174, 0.6035, 0.8790, 0.6109]],
    
            [[0.6634, 0.4503, 0.0455, 0.1194, 0.2872],
             [0.7791, 0.5386, 0.7341, 0.5429, 0.3587],
             [0.2208, 0.2353, 0.7941, 0.1140, 0.5503],
             [0.8110, 0.3359, 0.3486, 0.7482, 0.3088]],
    
            [[0.9698, 0.8568, 0.0981, 0.3720, 0.7700],
             [0.3536, 0.7100, 0.6332, 0.5188, 0.3759],
             [0.2450, 0.7529, 0.5502, 0.4768, 0.3968],
             [0.3272, 0.5425, 0.6530, 0.9158, 0.0884]]])
    torch.Size([3, 4, 5])
    tensor([[[0.4573],
             [0.4662],
             [0.6756],
             [0.5512]],
    
            [[0.3132],
             [0.5907],
             [0.3829],
             [0.5105]],
    
            [[0.6134],
             [0.5183],
             [0.4843],
             [0.5054]]])
    torch.Size([3, 4, 1])
    tensor([[0.4573, 0.4662, 0.6756, 0.5512],
            [0.3132, 0.5907, 0.3829, 0.5105],
            [0.6134, 0.5183, 0.4843, 0.5054]])
    torch.Size([3, 4])
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    2.4 Dropout层

    作用:减少过拟合

    按照指定概率,随机丢弃一些神经元(将其化为零)

    其余元素乘以 1 / (1 – p)进行放大(如果不放大的话,在量级上就会有差别)

    在这里插入图片描述

    #coding:utf8
    
    import torch
    import torch.nn as nn
    import numpy as np
    
    
    """
    基于pytorch的网络编写
    测试dropout层
    """
    
    import torch
    
    x = torch.Tensor([1,2,3,4,5,6,7,8,9])
    dp_layer = torch.nn.Dropout(0.5)# p=0.5 ,代入公式算出翻两倍
    dp_x = dp_layer(x)
    print(dp_x)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    如何理解其作用:

    1)强迫一个神经单元,和随机挑选出来的其他神经单元共同工作,消除减弱了神经元节点间的联合适应性,增强了泛化能力

    2)可以看做是一种模型平均,由于每次随机忽略的隐层节点都不同,这样就使每次训练的网络都是不一样的,每次训练都可以单做一个“新”的模型

    计算方式并不是越复杂就越好

    2.5 激活函数

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

    无激活函数时 y = w1(w2(w3 * x + b3) +b2) + b1 仍然是线性函数

    在这里插入图片描述

    2.5.1 Sigmoid

    将任意输入映射到0-1之间。函数值连续,可导

    缺点:

    1.计算耗时,包含指数运算

    2.非0均值,会导致收敛慢

    3.易造成梯度消失
    σ ( x ) = 1 1 + e − x σ(x)=\frac{1}{1+e^{-x}} σ(x)=1+ex1

    在这里插入图片描述

    2.5.2 tanh

    以0为均值,解决了sigmoid的一定缺点,但是依然存在梯度消失问题

    计算同样非常耗时
    t a n h ( x ) = s i n h ( x ) c o s h ( x ) = e x − e − x e x + e − x tanh(x)=\frac{sinh(x)}{cosh(x)}=\frac{e^x-e^{-x}}{e^x+e^{-x}} tanh(x)=cosh(x)sinh(x)=ex+exexex

    在这里插入图片描述

    2.5.3 Relu

    在正区间不易发生梯度消失

    计算速度非常快

    一定程度上降低过拟合的风险
    R e l u = m a x ( 0 , x ) Relu=max(0,x) Relu=max(0,x)

    在这里插入图片描述

    2.6 损失函数

    2.6.1 均方差

    MSE mean square error 对均方差在做开根号,可以得到根方差

    一般用于0~1之间的数字
    l ( x , y ) = L = { l 1 , ⋅ ⋅ ⋅ , l N } T , l n = ( x n − y n ) 2 l(x,y)=L=\{l_1,···,l_N\}^T,l_n=(x_n-y_n)^2 l(x,y)=L={l1,⋅⋅⋅,lN}T,ln=(xnyn)2

    l ( x , y ) = { m e a n ( L ) , i f r e d u c t i o n = ′ m e a n ′ s u m ( L ) , i f r e d u c t i o n = ′ s u m ′ l(x,y)= \left\{

    mean(L),ifreduction=meansum(L),ifreduction=sum" role="presentation">mean(L),ifreduction=meansum(L),ifreduction=sum
    \right. l(x,y)={mean(L),ifreduction=meansum(L),ifreduction=sum

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

    2.6.2 交叉熵

    Cross Entropy常用于分类任务

    分类任务中,网络输出经常是所有类别上的概率分布

    公式:
    H ( p , q ) = − ∑ x p ( x ) l o g q ( x ) H(p,q)=-\sum_{x}p(x)logq(x) H(p,q)=xp(x)logq(x)
    假设一个三分类任务,某样本的正确标签是第一类,则p = [1, 0, 0], 模型预测值假设为[0.5, 0.4, 0.1], 则交叉熵计算如下:
    H ( p = [ 1 , 0 , 0 ] , q = [ 0.5 , 0.4 , 0.1 ] ) = − ( 1 ∗ l o g 0.5 + 0 ∗ l o g 0.4 + 0 ∗ l o g 0.1 ) ≈ 0.3 H(p=[1,0,0],q=[0.5,0.4,0.1])=-(1*log0.5+0*log0.4+0*log0.1)≈0.3 H(p=[1,0,0],q=[0.5,0.4,0.1])=(1log0.5+0log0.4+0log0.1)0.3

    import torch
    import torch.nn as nn
    import numpy as np
    
    #使用torch计算交叉熵
    ce_loss = nn.CrossEntropyLoss()
    #假设有3个样本,每个都在做3分类
    pred = torch.FloatTensor([[0.0, 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()), "手动实现交叉熵")
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    2.6.3 其他

    指数损失:
    L ( Y , f ( X ) ) = e x p ( − Y f ( X ) ) L(Y,f(X))=exp(-Yf(X)) L(Y,f(X))=exp(Yf(X))
    对数损失
    L ( Y , P ( Y ∣ X ) ) = − l o g P ( Y ∣ X ) L(Y,P(Y|X))=-logP(Y|X) L(Y,P(YX))=logP(YX)
    0/1损失
    L ( Y , f ( X ) ) = { 1 , Y ≠ f ( X ) 0 , Y = f ( X ) L(Y,f(X))= \left\{

    1,Yf(X)0,Y=f(X)" role="presentation">1,Yf(X)0,Y=f(X)
    \right. L(Y,f(X))={1,Y=f(X)0,Y=f(X)

    Hinge损失(二分类)
    J h i n g e = ∑ i = 1 N m a x ( 0 , 1 − s g n ( y i ) y i ^ ) J_{hinge}=\sum^{N}_{i=1}max(0,1-sgn(y_i)\hat{y_i}) Jhinge=i=1Nmax(0,1sgn(yi)yi^)

    2.7 优化器

    根据梯度,控制调整权重的幅度

    2.7.1 SGD

    SGD (Stochastic gradient descent):权重=权重-学习率×梯度

    θ t + 1 = θ t − α ▽ L ( θ t ) θ_{t+1}=θ_t-α▽L(θ_t) θt+1=θtαL(θt)

    #梯度更新
    def diy_sgd(grad, weight, learning_rate):
        return weight - learning_rate * grad
    
    • 1
    • 2
    • 3

    2.7.2 Adam

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

    2.7.3 SGD与Adam对比

    权重=权重-学习率×梯度

    SGD:weight - learning_rate * grad

    Adam: weight - (alpha / (np.sqrt(vth) + eps)) * mth

    Adam优点:

    ①Adam的学习率为(alpha / (np.sqrt(vth) + eps)),是一个自动调整的过程

    ②Adam的梯度 mth = mt / (1 - beta1 ** t) 是与前面训练出来的梯度混合之后的梯度

    2.8 完整的反向传播过程

    1.根据输入x和模型当前权重,计算预测值y’

    2.根据y’和y使用loss函数计算loss

    3.根据loss计算模型权重的梯度

    4.使用梯度和学习率,根据优化器调整模型权重

        #手动实现梯度计算
        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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    redis的redis.service配置
    Highlight Insights with Conditional
    《设计模式》适配器模式
    基于Java星空游戏购买下载平台设计实现(源码+lw+部署文档+讲解等)
    说一下 toRef、toRefs,以及他们的区别
    Firefox火狐浏览器显示你的连接不安全,是什么意思?
    [附源码]java毕业设计静谧空间自习室预订系统
    接口自动化测试框架搭建全部过程
    redis配置到服务自启动——笔记
    NoSql 数据库简介
  • 原文地址:https://blog.csdn.net/m0_52923241/article/details/127494864