以下内容有任何不理解可以翻看我之前的博客哦:吴恩达deeplearning.ai
文章目录
- softmax作为输出层的神经网络
- Tensorflow的实现
- softmax的改进实现
- 数值舍入误差(Numerical Roundoff Errors)
- sigmoid修改
- 修改softmax
在上一篇博客中我们了解了有关softmax的原理相关内容,今天我们主要聚焦于如何修改之前的神经网络,从而搭建能够实现多分类问题的神经网络。
softmax作为输出层的神经网络
相比之前的二分类逻辑回归神经网络,我们主要的改变是将输出层替换为了具有十个神经元的,激活函数为softmax的输出层。整个神经网络的运行流程是接收特征输入X,并且传入隐藏层,两个隐藏层的激活函数均采用的是relu函数;再传入最终输出层,最终的输出
a
[
3
]
a^{[3]}
a[3]是一个包含十个概率值的矩阵。
我们再回顾下softmax的公式(这里仅以a1为例):
z
1
=
w
1
⃗
⋅
x
⃗
+
b
1
a
1
=
e
z
1
e
z
1
+
e
z
2
+
e
z
3
+
e
z
4
z_1=\vec{w_1}\cdot\vec{x}+b_1\\ a_1=\frac{e^{z_1}}{e^{z_1}+e^{z_2}+e^{z_3}+e^{z_4}}
z1=w1⋅x+b1a1=ez1+ez2+ez3+ez4ez1
此外提一个定义,softmax层有时也被叫做softmax函数。与其它的激活函数相比不同的是,softmax中
a
1
a_1
a1仅仅和
z
1
z_1
z1有关,
a
2
a_2
a2仅仅和
z
2
z_2
z2有关,而不像其它的激活函数最终的某个输出a和多个z有关。
让我们看看如何用代码实现这个神经网络
Tensorflow的实现
第一步,构建神经网络的结构框架:
import tensorflow as tf
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
model = Sequential([
Dense(units=25, activation='relu')
Dense(units=15, activation='relu')
Dense(units=10, activation='softmax')
])
第二步,定义损失函数和价值函数
from tensorflow.keras.losses import
SparseCategoricalCrossentropy
model.compile(loss=SparseCategoricalCrossentropy())
这里出现了一个新的函数SparseCategoricalCrossentropy(),翻译成中文叫做稀疏分类交叉熵,名字超长,甚至超过了当初的二元交叉熵。稀疏(Sparse)的意思是值只能取1~10中的一个;分类(Categorical)指的是你仍然将y分类。
第三步,训练模型,预测代码和以前一样:
model.fit(X, Y, epochs=100)
以上代码是可以起作用的,符合我们之前的认知,但是还不够优化,在tensorflow中有更好的代码版本。下面我们看看如何优化softmax代码。
softmax的改进实现
数值舍入误差(Numerical Roundoff Errors)
让我先展示下在计算机设置数值的两种不同方法:
第一种,简单粗暴法:
x
=
2
10
,
000
x=\frac{2}{10,000}
x=10,0002
第二种,加加减减法:
x
=
(
1
+
1
10
,
000
)
−
(
1
−
1
10
,
000
)
x=(1+\frac{1}{10,000})-(1-\frac{1}{10,000})
x=(1+10,0001)−(1−10,0001)
虽然看上去相同,但是精确度是由差别的:
我们对softmax的改进,也主要聚焦在精确度上面,让我介绍一种更加精确的方法。
sigmoid修改
在逻辑回归中,我们的公式是这样的:
a
=
g
(
z
)
=
1
1
+
e
−
z
l
o
s
s
=
−
y
l
o
g
a
−
(
1
−
y
)
l
o
g
(
1
−
a
)
a=g(z)=\frac{1}{1+e^-z}\\ loss=-yloga-(1-y)log(1-a)
a=g(z)=1+e−z1loss=−yloga−(1−y)log(1−a)
它的代码是:
model = Sequential([
Dense(units=25, activation='relu')
Dense(units=15, activation='relu')
Dense(units=10, activation='sigmoid')
])
model.compile(loss=BinaryCrossEntropy())
如果我们要求tensorflow按照这个步骤,一步步算出a,然后带入到loss之中,那么结果就会如同上面的第二种方法一样产生误差,因为其进行了两步运算。但是tensorflow提供了另一种方法,大致意思就是我们先使用线性激活函数(也可以理解为没使用激活函数),最后在计算损失的时候再指定激活函数为sigmoid。如果我们使用了这个命令,这会为tensorflow提供更高的灵活性,从而可以减少误差,就如同上面的方法一;代码如下:
model = Sequential([
Dense(units=25, activation='relu')
Dense(units=15, activation='relu')
Dense(units=10, activation='linear')
])
model.compile(loss=BinaryCrossEntropy(from_logits=True))
通俗点说from_ligits=True告诉了激活函数inaryCrossEntropy我没有用激活函数哦,所以你计算损失时内部记得调用下sigmoid哈。这里的logits可以理解为没有经过激活函数的z。
修改softmax
同样地,我们再看看稀疏分类交叉熵的损失函数,我就写出其中的一项:
L
o
s
s
=
−
l
o
g
a
i
f
y
=
1
Loss=-loga\:ify=1
Loss=−logaify=1
由于在多分类问题之中,分类的选项很多,而各个选项的概率和是一定的为1,因此很多情况下正确的那个选项的概率依然很小,由于使用了log函数,在x接近于0的时候这个值会非常大,那么产生的误差也就会很大,而二分类问题由于选项仅有两个,因此这个问题不是很明显,便没在讲二分类的时候也进行这种优化。
一样地,我们代码也可以修改为:
model = Sequential([
Dense(units=25, activation='relu')
Dense(units=15, activation='relu')
Dense(units=10, activation='linear')
])
from tensorflow.keras.losses import
SparseCategoricalCrossentropy
model.compile(loss=SparseCategoricalCrossentropy(from_logits=True))
另外需要修改的地方是,我们在预测时,model(x)不再是概率a了,而是没经过激活函数的z,因此代码在最后需要添加:
model.fit(X, Y, epochs=100)
logits = model(X)
f_x = tf.nn.softmax(logits)
从而再加入了softmax,出来的才是0~1之间的概率a。
为了给读者你造成不必要的麻烦,博主的所有视频都没开仅粉丝可见,如果想要阅读我的其他博客,可以点个小小的关注哦。