今天在阅读深度学习入门鱼书的时候,读到4.2.4mini-batch版交叉熵误差的实现这一部分时,对其部分的代码有一点不解,虽然笔者有详细的解释,但是仍然存在一些问题不懂,在查阅了别人的解读后明白了,最后打算记录一下。
一、交叉熵损失是什么?
在神经网络的训练过程中,我们使用损失函数用来判断模型的拟合效果。损失函数可以使用任意的函数,但是我们在深度学习中通常使用均方误差(Mean Squared Error, MSE)和交叉熵误差(Cross Entropy Error)。在分类问题中,通常我们选择交叉熵误差。本章的内容是基于MINSTS手写数字识别。交叉熵误差如下式所示:
二、使用步骤
数据部分(之前因为基础太差,在写书上代码的时候想自己写一组数据,但是老报错,所以引用了别人代码http://t.csdn.cn/t8AOe):
import numpy as np
y=np.array([[0,1,0.1,0,0,0,0,0,0,0],
[0,0,0.2,0.8,0,0,0,0,0,0]])
t_onehot=np.array([[0,1,0,0,0,0,0,0,0,0],
[0,0,0,1,0,0,0,0,0,0]])#one-hot
t = t_onehot.argmax(axis=1)#非one-hot,这一步是将one—hot里为1的项的索引提出来
print(t)#[1 3]
batch_size = y.shape[0]
print(batch_size)#2
k=y[np.arange(batch_size), t] # [y[0,1] y[1,3]]
print(k)#[1. 0.8]
log=np.log(k)
print(log)#[ 0. -0.22314355]
r=-np.sum(np.log(y[np.arange(batch_size), t] + 1e-7))/ batch_size
print(r)#0.11157166315711126
之后就是在读到 这一行代码的时候,虽然笔者说的很明了:(以下内容为书中原话)
np.arange(batch.size)会生成一个从0到batch_size-1的数组。比如当batch_size为5时,np.arange(batch.size)会生成一个NumPy数组[0,1,2,3,4]。因为t中标签是以[2,7,0,9,4]的形式存储的,所以y[np.arange(batch.size),t]能抽出各个数据的正确标签对应的神经网络的输出(在这个例子中,y[np.arange(batch.size),t]会生成NumPy数组y[0,2],y[1,7],y[2,0],y[3,9],y[4,4])。
-np.sum(np.log(y[np.arange(batch_size), t] + 1e-7))/ batch_size
但是我在看到这句代码的时候仍然有很多疑问,就算生成了这些数组,那将这些数组放入log中,又有什么用呢?于是我查找了一些博客,了解了其中的原理(http://t.csdn.cn/w4ZVL):
虽然在这里我们采用的监督数据为标签形式,但是我们仍然需要先写出他的独热编码(因为需要通过其中不为0的元素提取出索引,构成标签(0,1,2...,9))。如本文中的实例:
我们构建出了输出的数据,这里的batch_size为2,即代表可以识别两个数字的模型。可以看出第一组索引为‘1’的元素为1,第二组中索引为‘3’的的元素为0.8,我们可以知道其识别出的数字为1,3,但是我们要怎么提取出这个1,3呢?
我们采用argmax()函数将其索引提出来就可以啦
t = t_onehot.argmax(axis=1)
提取出来的结果就是[1 3]
最后我们再来说说这里的y[np.arange(batch_size), t]。正如书中所说,这一步骤是将生成的batch_size大小的数组和t拼接起来,所以这里生成的数组就是y[0,1],y[1,3]。我之前也因为基础的问题在这里犯了错误,其实这里就是很简单的在数组y中拿出第0行一列和第一行三列的的值
就是这里的1和0.8
然后对他们进行交叉熵损失求和就是最后的结果了。
但是在这里得到[1. 0.8]之后我之前还是看不懂log[1. 0.8]的意思。其实很简单的,numpy里的函数都是可以处理数组的,输入数组输出也是数组。最后的结果就是将他们相加然后处于batch_size求均值啦!
代码实现
最后在了解了相关函数后,我们用自己编写的数据实现书中的代码吧!
import numpy as np
y = np.array([[0.1,0.05,0.6,0.0,0.05,0.1,0.0,0.1,0.0,0.0],
[0.8,0.1,0.0,0.0,0.05,0.0,0.0,0.05,0.0,0.0],
[0.0,0.1,0.1,0.0,0.0,0.05,0.05,0.7,0.0,0.0],
[1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0],
[0.2,0.2,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.6]])
t_one_hot = np.array([[0,0,1,0,0,0,0,0,0,0],
[1,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,1]]) #one-hot
t = t_one_hot.argmax(axis=1)
print(t)
def cross_entropy_error_one_hot(y,t_one_hot): #独热编码的交叉熵损失
if y.ndim == 1:
t_one_hot = t_one_hot.reshape(1, t_one_hot.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
return -np.sum(t_one_hot * np.log(y + 1e-7)) / batch_size
def cross_entropy_error(y,t): #标签形式的交叉熵损失
if y.ndim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size #y[0,2] y[1,0] y[2,7] y[3,4] y[4 9]
print("loss by one_hot= :" , cross_entropy_error_one_hot(y, t_one_hot))
print("loss = :" , cross_entropy_error(y, t))
这里的数据是我自己瞎想的,只要满足和为1就可以了嘛,因为要原原本本的复现MNIST,还需要下载数据集,我这里偷懒就不做了,最后我们得到的结果如图:
可以看到我们的结果是一样的!
以上就是全部内容了,博主也只是刚入门深度学习的小萌新(可能连入门还算不上),一直都想有自己的方式记录学习的过程,以上内容也是我查阅别人的博客后自己写出来的一些感想吧,学习永远都没有捷径,只能一步步的来,虽然这只是一个非常简单的小问题,但是我还是决定记录一下,至少证明我们曾来过~