锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

数字识别之---介绍损失函数

时间:2022-10-14 14:30:00 kyk连接器

损失函数:

模型优化的目标是识别许多参数值中最理想的值。在训练过程的代码中计算损失函数,每轮模型训练过程相同,分为以下三个步骤:
1.首先根据输入数据预测输出

2.根据预测值和真实值计算损失

3.最后,根据损失反向传输梯度更新参数

损失函数的选择:

不同的深度学习任务需要有自己合适的损失函数。例如,在房价预测模型中,输入是13x来预测房价,即输出是y。这项任务显然属于回归任务。在手写数字识别任务中,输入28*28图像输出其标签,即0~9之间的10个整数属于分类任务。

使用均方误差作为分类任务的损失函数存在逻辑和效果上的欠缺。

在房价预测的情况下,由于房价本身是一个连续的实际值,模型输出值和实际价格差距被用作损失函数(LOSS)是符合道理的。但对于分类问题,真实结果是分类标签,而模型输出是实数值,导致两者相减作为损失不具备意义。

那么,分类任务的合理输出是什么呢?分类任务本质上是特征组合下的分类概率,以下是一个简单的案例描述。如图所示

图 :观测数据与背后规律的关系

在这种情况下,医生根据肿瘤x作为肿瘤性质y的参考判断(判断因素很多,肿瘤大小只是其中之一),因此我们观察到该模型的结果是x和y标签(1为恶性,0为良性)。该数据背后的规律是不同大小的肿瘤,属于恶性肿瘤的概率。观测数据是抽样真实规律的结果,分类模型应拟合真实规律,输出属于分类标签的概率。

Softmax函数:

如果模型能够输出10个标签的概率,则真实标签的概率输出应尽可能接近100%,而其他标签的概率输出应尽可能接近0%,所有输出概率之和应为1。这是一个更合理的假设!相应地,真实的标签值可以转换为10维one-hot在对应数字的位置上,向量为1,其余位置为0,如标签6可转换为[0、0、0、0、0、1、0、0、0、0]。

为了实现上述思路,需要引入Softmax函数可以将原始输出转换为相应标签的概率,公式如下CCC是标签类别的数量。

从公式的形式可以看出,每个输出的范围为0~1之间,所有输出之和等于1。对应于代码,我们需要在网络定义部分修改输出层:self.fc = FC(name_scope,size=10,act='softmax即全连接层FC加一个输出softmax运算。

下图使用了三个标签的分类模型softmax从输出层可以看出,原始输出的数字是3、1、-3,通过softmax三个概率值0转换为加和1.88、0.12.、0.

图 :网络输出层改为softmax函数

交叉熵

当模型输出是分类标签的概率时,直接使用标签和概率是不合适的,人们更习惯于使用交叉熵误差作为分类问题的损失衡量。

交叉熵的公式如下

其中,log\loglog表示以eee自然对数是底数。yky_kyk代表模型输出,tkt_ktk代表各个标签。tkt_ktk只有正确的标签是1,其余的是0(one-hot表示)。

因此,交叉熵只计算对应正确解标签输出的自然对数。例如,假设正确标签的索引是2.6.交叉熵误差为log0.6=0.51?\log 0.6 = 0.51?log0.6=0.51;如果2对应的输出为0.1.交叉熵误差为log0.1=2.30?\log 0.1 = 2.30?log0.1=2.30。由此可见,交叉熵误差的值是由正确标签对应的输出结果决定的。

以下代码可以实现自然对数的函数曲线。

import matplotlib.pyplot as plt import numpy as np x = np.arange(0.01,1,0.01) y = np.log(x) plt.title("y=log(x)")  plt.xlabel("x")  plt.ylabel("y")  plt.plot(x,y) plt.show() plt.figure()

结果如下所示

如自然对数图所示,当x等于1时,y为0;随着x向0,y逐渐变小。因此,标签对应的输出越大,交叉熵值越接近0;当输出为1时,交叉熵误差为0。相反,标签对应的输出越小,交叉熵值越大。

实现交叉熵代码

需要注意的是,‘需要注意的是’

  • 在读取数据部分,需要修改标签类型,并将标签类型设置为int,这反映了它是一个标签,而不是一个真实的值(桨框默认将标签处理成标签)Integrate4).
  • 在网络定义部分,将输出层改为输出十个标签的概率模式
  • 在训练过程中,将损失函数从均方误差转换为交叉熵

在数据处理部分,需要修改标签变量Label代码如下:

从label=np.reshape(labels[i],[1].astype('float32'))

到label=np.reshape(labels[i],[1].astype('int64'))

#从float32到int64 import os import random import paddle import paddle.fluid as fluid from paddle.fluid.dygraph.nn import Conv2D, Pool2D, Linear import numpy as np from PIL import Image  import gzip import json  # 定义数据集读取器 def load_data(mode='train'):      # 数据文件     datafile = './work/mnist.json.gz'     print('loading mnist dataset from {} ...'.format(datafile))     data = json.load(gzip.open(datafile))     train_set, val_set, eval_set = data      # 数据集相关参数,图片高度IMG_ROWS, 图片宽度IMG_COLS     IMG_ROWS = 28     IMG_COLS = 28      if mode == 'train':         imgs = train_set[0]         labels = train_set[1]     elif mode == 'valid':         imgs = val_set[0]         labels = val_set[1]     elif mode == 'eval':         imgs = eval_set[0]         labels = eval_set[1]      imgs_length = len(imgs)      assert len(imgs) == len(labels), \           "length of train_imgs({}) should be the same as train_labels({})".format(                   len(imgs), len(labels))      index_list = list(range(imgs_length))      # 读取数据时使用的batchsize     BATCHSIZE = 100      # 定义数据生成器     def data_geerator():
        if mode == 'train':
            random.shuffle(index_list)
        imgs_list = []
        labels_list = []
        for i in index_list:
            img = np.reshape(imgs[i], [1, IMG_ROWS, IMG_COLS]).astype('float32')
            label = np.reshape(labels[i], [1]).astype('int64')
            imgs_list.append(img) 
            labels_list.append(label)
            if len(imgs_list) == BATCHSIZE:
                yield np.array(imgs_list), np.array(labels_list)
                imgs_list = []
                labels_list = []

        # 如果剩余数据的数目小于BATCHSIZE,
        # 则剩余数据一起构成一个大小为len(imgs_list)的mini-batch
        if len(imgs_list) > 0:
            yield np.array(imgs_list), np.array(labels_list)

    return data_generator

在网络定义部分,需要修改输出层结构,代码如下:

从self.fc=FC(name_scope,size=1,act=None)

到self.fc=FC(name_scope,size=1,act='softmax')

# 定义模型结构
class MNIST(fluid.dygraph.Layer):
     def __init__(self, name_scope):
         super(MNIST, self).__init__(name_scope)
         name_scope = self.full_name()
         # 定义一个卷积层,使用relu激活函数
         self.conv1 = Conv2D(num_channels=1, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
         # 定义一个池化层,池化核为2,步长为2,使用最大池化方式
         self.pool1 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
         # 定义一个卷积层,使用relu激活函数
         self.conv2 = Conv2D(num_channels=20, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
         # 定义一个池化层,池化核为2,步长为2,使用最大池化方式
         self.pool2 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
         # 定义一个全连接层,输出节点数为10 
         self.fc = Linear(input_dim=980, output_dim=10, act='softmax')
    # 定义网络的前向计算过程
     def forward(self, inputs):
         x = self.conv1(inputs)
         x = self.pool1(x)
         x = self.conv2(x)
         x = self.pool2(x)
         x = fluid.layers.reshape(x, [x.shape[0], 980])
         x = self.fc(x)
         return x

修改损失函数,从均方误差(常用于回归问题)到交叉熵(常用于分类问题),代码如下所示

从loss=fluid.layers.square_error_cost(predict,label)

到loss=fluid.layers.cross_entropy(predict,label)

#仅修改计算损失的函数,从均方误差(常用于回归问题)到交叉熵误差(常用于分类问题)
with fluid.dygraph.guard():
    model = MNIST("mnist")
    model.train()
    #调用加载数据的函数
    train_loader = load_data('train')
    optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01, parameter_list=model.parameters())
    EPOCH_NUM = 5
    for epoch_id in range(EPOCH_NUM):
        for batch_id, data in enumerate(train_loader()):
            #准备数据,变得更加简洁
            image_data, label_data = data
            image = fluid.dygraph.to_variable(image_data)
            label = fluid.dygraph.to_variable(label_data)
            
            #前向计算的过程
            predict = model(image)
            
            #计算损失,使用交叉熵损失函数,取一个批次样本损失的平均值
            loss = fluid.layers.cross_entropy(predict, label)
            avg_loss = fluid.layers.mean(loss)
            
            #每训练了200批次的数据,打印下当前Loss的情况
            if batch_id % 200 == 0:
                print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, avg_loss.numpy()))
            
            #后向传播,更新参数的过程
            avg_loss.backward()
            optimizer.minimize(avg_loss)
            model.clear_gradients()

    #保存模型参数
    fluid.save_dygraph(model.state_dict(), 'mnist')

 

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章