神经网络优化(二) - 激活函数和损失函数

本文深入探讨神经网络中的核心概念——激活函数与损失函数,介绍ReLU、Sigmoid、Tanh等常见激活函数及其作用,以及均方误差、自定义损失函数和交叉熵在模型优化中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 神经网络中的激活函数activation function

1.1 引入激活函数概念

神经网络的基本构成单元是神经元。 

搭建神经网络一文中使用的神经元模型

这个神经元模型是较为简化的基本神经元模型。还有一种理论模型包含有激活函数偏置项的神经元模型

1943年McCulloch 和 Pitts 提出了一种神经元模型,这也就是McCulloch-Pitts神经元模型:

该神经元模型中的数学公式表示为?(∑????  + ?),f为激活函数

激活函数:引入非线性激活因素,避免∑????  + ?纯线性组合的缺陷,提高了模型的表达力,使模型具有更好的区分度。

1.2 常见的激活函数

常用的激活函数有 relu、sigmoid、tanh

 

1.2.1 激活函数relu

(1)tf中表达式: tf.nn.relu() 

(2)数学表达式

(3)函数图像

1.2.2 激活函数sigmoid

(1)Tensorflow中表达式: tf.nn.sigmoid() 

(2)数学表达式

(3)函数图像

1.2.3 激活函数tanh

(1)Tensorflow中表达式: tf.nn.tanh() 

(2)数学表达式

(3)函数图像

2 损失函数loss

损失函数:前向传播生成的预测值 (y) 与 标签 (y_) 之间的差距。

NN的优化目标:获得具有最小loss的神经网络模型。在训练神经网络时,就是通过不断改变神经网络中的权重参数、偏置量等参数,使损失函数不断减小,从而训练出更高准确率的神经网络模型。

常用的损失函数有均方误差mse(Mean Squared Error)、自定义和交叉熵ce(Cross Entropy)等

2.1 均方误差mse

2.1.1 均方误差的公式

假设有n个样本,其对应标签为y_,经神经网络计算的预测值为y。

(1)均方误差定义:

预测值y与标签y_差值平方和的平均数

(2)数学公式

(3)Tensorflow公式

loss_mse = tf.reduce_mean(tf.square(y_ - y))

2.1.2 均方误差的实例应用

拟合预测酸奶日销量的函数;便于准确预测销量后指导产量(最佳 销量=产量)。

假设预测酸奶日销量为y,影响日销量的两个因素x1、x2。并有采集的每日因素 x1、x2 和销量 y_ 历史数据。

(在实际上,并没有酸奶相关数据,所以需要拟造一套数据集并进行神经网络训练)

# 第一步 导入模块,生成数据集
import tensorflow as tf
import numpy as np
BATCH_SIZE = 8
SEED = 23455

rdm = np.random.RandomState(SEED)
# 拟为用32组数据训练神经网络,有2个特征参数
X = rdm.rand(32,2)
# 生成数据集标签,
# rdm.rand()函数返回值在0~1之间,确切地讲位于0.1~1之间,
# rdm.rand()/10.0-0.05的结果相对x1、x2来讲低一个量级,
# 这里表示仅对x1+x2的值微调。
Y_ = [[x1+x2+(rdm.rand()/10.0-0.05)] for (x1, x2) in X]

# 第二步 定义前向传播过程
# placeholder为喂入数据集及数据标签占位
x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
# 生成前向传播时所需权重参数赋值,标准差stddev=1
# 由于该神经网络为一层,所以仅赋一个权重参数即可
w1= tf.Variable(tf.random_normal([2, 1], stddev=1, seed=1))
# 矩阵运算,两个矩阵乘积
y = tf.matmul(x, w1)

# 第三步 损失函数及反向传播过程
# 选用均方误差MSE作为损失函数
loss_mse = tf.reduce_mean(tf.square(y_ - y))
# 选用梯度下降法作为权重优化函数
train_step = tf.train.GradientDescentOptimizer(0.001).minimize(loss_mse)

#第四步 生成会话,训练STEPS轮
with tf.Session() as sess:
    # 全局变量初始化
    init_op = tf.global_variables_initializer()
    sess.run(init_op)
    # 进行循环优化权重参数,20000轮
    STEPS = 20000
    for i in range(STEPS):
        # start为0、8、16、24
        start = (i*BATCH_SIZE) % 32
        # 每次喂入BATCH_SIZE数据量
        end = start + BATCH_SIZE
        # 执行数据训练过程,train_step权重优化函数,喂入数据集及对应标签
        sess.run(train_step, feed_dict={x: X[start:end], y_: Y_[start:end]})
        
        # 每循环500次打印一下权重参数训练效果,
        if i % 500 == 0:
            print("After %d training steps, w1 is: " % (i))
            print(sess.run(w1), "\n")
            # 计算损失函数值,观察损失函数随训练次数的变化趋势
            totle_mse = sess.run(loss_mse, feed_dict={x: X, y_: Y_})
            print(totle_mse, "\n")
    print("Final w1 is: \n", sess.run(w1))

运行

After 0 training steps, w1 is: 
[[-0.80974597]
 [ 1.4852903 ]] 

0.6557005 

After 500 training steps, w1 is: 
[[-0.46074435]
 [ 1.641878  ]] 

0.35731 

After 1000 training steps, w1 is: 
[[-0.21939856]
 [ 1.6984766 ]] 

0.23248056 

After 1500 training steps, w1 is: 
[[-0.04415595]
 [ 1.7003176 ]] 

0.17040439 

After 2000 training steps, w1 is: 
[[0.08942621]
 [1.673328  ]] 

0.13303667 

After 2500 training steps, w1 is: 
[[0.19583555]
 [1.6322677 ]] 

0.10693912 

After 3000 training steps, w1 is: 
[[0.28375748]
 [1.5854434 ]] 

0.08706193 

After 3500 training steps, w1 is: 
[[0.35848638]
 [1.5374471 ]] 

0.071270935 

After 4000 training steps, w1 is: 
[[0.4233252]
 [1.4907392]] 

0.05849066 

After 4500 training steps, w1 is: 
[[0.48040032]
 [1.4465573 ]] 

0.048065245 

After 5000 training steps, w1 is: 
[[0.5311361]
 [1.4054534]] 

0.0395331 

After 5500 training steps, w1 is: 
[[0.57653254]
 [1.367594  ]] 

0.032540925 

After 6000 training steps, w1 is: 
[[0.6173259]
 [1.3329402]] 

0.02680777 

After 6500 training steps, w1 is: 
[[0.65408474]
 [1.3013425 ]] 

0.022105861 

After 7000 training steps, w1 is: 
[[0.68726856]
 [1.2726018 ]] 

0.018249318 

After 7500 training steps, w1 is: 
[[0.7172598]
 [1.2465004]] 

0.015086038 

After 8000 training steps, w1 is: 
[[0.74438614]
 [1.2228196 ]] 

0.012491369 

After 8500 training steps, w1 is: 
[[0.7689325]
 [1.2013482]] 

0.0103631 

After 9000 training steps, w1 is: 
[[0.79115146]
 [1.1818888 ]] 

0.00861741 

After 9500 training steps, w1 is: 
[[0.81126714]
 [1.1642567 ]] 

0.0071855206 

After 10000 training steps, w1 is: 
[[0.8294814]
 [1.1482829]] 

0.0060110046 

After 10500 training steps, w1 is: 
[[0.84597576]
 [1.1338125 ]] 

0.0050475756 

After 11000 training steps, w1 is: 
[[0.8609128]
 [1.1207061]] 

0.00425734 

After 11500 training steps, w1 is: 
[[0.87444043]
 [1.1088346 ]] 

0.0036091362 

After 12000 training steps, w1 is: 
[[0.88669145]
 [1.0980824 ]] 

0.0030774516 

After 12500 training steps, w1 is: 
[[0.8977863]
 [1.0883439]] 

0.0026413407 

After 13000 training steps, w1 is: 
[[0.9078348]
 [1.0795243]] 

0.002283624 

After 13500 training steps, w1 is: 
[[0.91693527]
 [1.0715363 ]] 

0.001990209 

After 14000 training steps, w1 is: 
[[0.92517716]
 [1.0643018 ]] 

0.0017495381 

After 14500 training steps, w1 is: 
[[0.93264157]
 [1.0577497 ]] 

0.0015521289 

After 15000 training steps, w1 is: 
[[0.9394023]
 [1.0518153]] 

0.0013901924 

After 15500 training steps, w1 is: 
[[0.9455251]
 [1.0464406]] 

0.0012573693 

After 16000 training steps, w1 is: 
[[0.95107025]
 [1.0415728 ]] 

0.0011484219 

After 16500 training steps, w1 is: 
[[0.9560928]
 [1.037164 ]] 

0.0010590521 

After 17000 training steps, w1 is: 
[[0.96064115]
 [1.0331714 ]] 

0.0009857531 

After 17500 training steps, w1 is: 
[[0.96476096]
 [1.0295546 ]] 

0.0009256217 

After 18000 training steps, w1 is: 
[[0.9684917]
 [1.0262802]] 

0.0008763098 

After 18500 training steps, w1 is: 
[[0.9718707]
 [1.0233142]] 

0.00083585805 

After 19000 training steps, w1 is: 
[[0.974931 ]
 [1.0206276]] 

0.00080267614 

After 19500 training steps, w1 is: 
[[0.9777026]
 [1.0181949]] 

0.0007754609 

[Finished in 11.7s]
运行结果代码

为了便于对比观察,对比头2次和尾2次的数据

After 0 training steps, w1 is: 
[[-0.80974597]
 [ 1.4852903 ]] 

0.6557005 

After 500 training steps, w1 is: 
[[-0.46074435]
 [ 1.641878  ]] 

0.35731 

……
After 19000 training steps, w1 is: 
[[0.974931 ]
 [1.0206276]] 

0.00080267614 

After 19500 training steps, w1 is: 
[[0.9777026]
 [1.0181949]] 

0.0007754609 

此时耗时Finished in 11.7s

也可以将损失函数计算 totle_mse = sess.run(loss_mse, feed_dict={x: X, y_: Y_}) 放在 if i % 500 == 0 外面,耗时 [Finished in 18.2s],这间接说明两种情况 loss_mse 的执行次数是不一样的。

在 if i % 500 == 0 外面时,每次 for i in range(STEPS) 都会运行一次,而在其里面是每隔500次运行一次。

 

注意(没有完全想明白)

 

1 loss_mse = tf.reduce_mean(tf.square(y_ - y))
 
2 totle_mse = sess.run(loss_mse, feed_dict={x: X, y_: Y_})
3 print(totle_mse, "\n")

loss_mse的计算代码中仅和 y_ 值有关,若将第二行代码修改为

totle_mse = sess.run(loss_mse, feed_dict={y_: Y_})

会报错

InvalidArgumentError (see above for traceback): You must feed a value for placeholder tensor 'Placeholder' with dtype float and shape [?,2]
     [[Node: Placeholder = Placeholder[dtype=DT_FLOAT, shape=[?,2], _device="/job:localhost/replica:0/task:0/device:CPU:0"]()]]

开始意为是Y_值的求算与X有关

Y_ = [[x1+x2+(rdm.rand()/10.0-0.05)] for (x1, x2) in X]

对Y_值进行修改,变成与X无关的变量。结果还是报同样的错误。

 

又仔细观察了报错说明(InvalidArgumentError )和位置(下图两个位置)。

初步推断可能是在进行会话运行时,必须对所有初始化变量中的placeholder 进行 feed a value

 2.2 自定义误差

采用均方误差时,只是建立产量-销量的关系,当且仅当成本 = 利润时,该关系模型才有可能实现利润最大化。

  • 预测多了( y ≥ y_ ),损失成本COST
  • 预测少了( y <y_ ),损失利润PROFIT

若建立成本-产量-销量-利润关系模型,实现利润最大化,同时人具体核算的任务量就会降低。

 

2.2.1 建立数学模型

自定义损失函数

(1)建立初步模型

 (2)模型细化

 

 (3)转化成Tensorflow语句

loss = tf.reduce_sum(tf.where(tf.greater(y,y_), COST*(y-y_), PROFIT*(y_-y)))

 

2.2.2 自定义损失函数的实例应用

一个单位量的酸奶成本COST为1元,利润PROFIT为9元

预测少一个单位量的酸奶则损失利润9元

预测多一个单位量的酸奶则损失利润1元

此时生成的函数应该是往多了的预测,也即倾向于 ‘ 微多 ’ 

注:下列代码仅修改了损失函数部分,在引入库部分增加了常量COST、PEROFIT赋值代码。 

# 第一步 导入模块,生成数据集
import tensorflow as tf
import numpy as np
BATCH_SIZE = 8
SEED = 23455
COST = 1
PROFIT = 9

rdm = np.random.RandomState(SEED)
# 拟为用32组数据训练神经网络,有2个特征参数
X = rdm.rand(32,2)
# 生成数据集标签,
# rdm.rand()函数返回值在0~1之间,确切地讲位于0.1~1之间,
# rdm.rand()/10.0-0.05的结果相对x1、x2来讲低一个量级,
# 这里表示仅对x1+x2的值微调。
Y_ = [[x1+x2+(rdm.rand()/10.0-0.05)] for (x1, x2) in X]

# 第二步 定义前向传播过程
# placeholder为喂入数据集及数据标签占位
x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
# 生成前向传播时所需权重参数赋值,标准差stddev=1
# 由于该神经网络为一层,所以仅赋一个权重参数即可
w1= tf.Variable(tf.random_normal([2, 1], stddev=1, seed=1))
# 矩阵运算,两个矩阵乘积
y = tf.matmul(x, w1)

# 第三步 损失函数及反向传播过程
# 选用自定义损失函数作为条件
loss_user_def = tf.reduce_sum(tf.where(tf.greater(y,y_), COST*(y-y_), PROFIT*(y_ - y)))
# 选用梯度下降法作为权重优化函数
train_step = tf.train.GradientDescentOptimizer(0.001).minimize(loss_user_def)

#第四步 生成会话,训练STEPS轮
with tf.Session() as sess:
    # 全局变量初始化
    init_op = tf.global_variables_initializer()
    sess.run(init_op)
    # 进行循环优化权重参数,20000轮
    STEPS = 20000
    for i in range(STEPS):
        # start为0、8、16、24
        start = (i*BATCH_SIZE) % 32
        # 每次喂入BATCH_SIZE数据量
        end = start + BATCH_SIZE
        # 执行数据训练过程,train_step权重优化函数,喂入数据集及对应标签
        sess.run(train_step, feed_dict={x: X[start:end], y_: Y_[start:end]})
        
        # 每循环500次打印一下权重参数训练效果,
        if i % 500 == 0:
            print("After %d training steps, w1 is: " % (i))
            print(sess.run(w1), "\n")
            # 计算损失函数值,观察损失函数随训练次数的变化趋势
            totle_mse = sess.run(loss_user_def, feed_dict={x: X, y_: Y_})
            print(totle_mse, "\n")
    print("Final w1 is: \n", sess.run(w1))

运行结果(略),W1最终值 ‘ 略大’;成本低,利润高,多产些、浪费点无所谓。

[[1.0225189]
 [1.0416601]]

若将数值更变

COST = 9
PROFIT = 1

运行结果(略),W1最终值 ‘ 略小’;成本高,利润低,少产些、浪费点会增加成本。

 [[0.9661967 ]
 [0.97694933]]

2.3 交叉熵ce(Cross Entropy)

交叉熵表示两个概率分布之间的距离。

交叉熵越大,概率间距越远,两概率分布越相异;

交叉熵越小,概率间距越近,两概率分布越相似;

 (1)数学表达式:

(2)Tensorflow语句

ce = - tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-12, 1.0)))

备注:

为了保障tf.log有意义,当 y 值小于1e-12时,将 y 值 clip 为 1e-12,当 y 值大于 1 时,将 y 值 clip 为 1 。

示例:

在二分类问题方面,已知标准答案为y_ = (1,0),现有两个预测结果y1 = (0.6,0.4)、y2 = (0.8,0.2),那个结果更接近标准答案呢?

根据交叉熵ce公式计算得:

H1((1,0),(0.6,0.4)) = -(1*log0.6 + 0*log0.4)  ≈ -(-0.222 + 0) = 0.222

H2((1,0),(0.8,0.2)) = -(1*log0.8 + 0*log0.2)  ≈ -(-0.097 + 0) = 0.097

所有y2预测更准。

2.4 softmax 函数

2.4.1 函数的基本释义

摘录于维基百科 - Softmax函数

 softmax函数(也称为归一化指数函数)是逻辑函数的一种推广。

它能将一个含任意实数的 K 维向量 z "压缩"到另一个 k 维实向量 σ(z) 中,使得每一个元素的范围都在 (0,1) 之间,并且所有元素的和为 1 ,该函数(softmax函数)的形式通常按下面的式子给出:

 softmax函数实际上是有限项离散概率分布的梯度对数归一化。因此,softmax函数在包含多项逻辑回归、多项线性判别分析、朴素贝叶斯分类器和人工神经网络等的多种基于概率的多分类问题方法中都有着广泛应用。特别地,在多项逻辑回归和线性判别分析中,函数的输入是从 k 个 不同的线性函数得到的结果,而样本向量 x 属于第 j 个分类的概率为:

这可以被视作 k 个线性函数 x   |→   xTw1, . . . , x   |→   xTwk Softmax函数的复合(xTwxw)

2.4.2 函数的示例及代码诠释

输入向量 [1,2,3,4,1,2,3] 对应的Softmax函数的值为  [0.024,0.064,0.175,0.475,0.024,0.064,0.175] 。输出向量中拥有最大权重的项对应着输入向量中的最大值“4”。这也显示了这个函数通常的意义:对向量进行归一化,凸显其中最大的值并抑制远低于最大值的其他分量。

代码

下面是使用Python进行函数计算的示例代码:

import math
z = [1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0]
z_exp = [math.exp(i) for i in z]  
print(z_exp)  # Result: [2.72, 7.39, 20.09, 54.6, 2.72, 7.39, 20.09] 
sum_z_exp = sum(z_exp)  
print(sum_z_exp)  # Result: 114.98 
softmax = [round(i / sum_z_exp, 3) for i in z_exp]
print(softmax)  # Result: [0.024, 0.064, 0.175, 0.475, 0.024, 0.064, 0.175]

Python使用numpy计算的示例代码:

import numpy as np
z = np.array([1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0])
print(np.exp(z)/sum(np.exp(z)))

 

 2.4.3 Tensorflow中的实现形式

该部分是这个函数在本小节的重点,2.4.1 和 2.4.2 是辅助理解过程。

ce = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
cem = tf.reduce_mean(ce)

 2018-10-28 23:28:31 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值