深度学习入门:return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

缘由:在学习深度学习入门:基于Python的理论与实现时,没有看懂第四章交叉熵误差函数,当监督数据采用非one-hot表示时。所以做了一个小测试案例,终于明白了,花了我几个小时的时间,学习不能着急呀。

一、结果和细节分析
注:使用mnist手写数字体数据集。

(1)、交叉熵误差函数
输入数据只有一个时:y和t都是1*n的矩阵,只需要求一行的相应和就可以得到这个数据对应的误差。
交叉熵误差函数
采用mini_batch批处理N个数据时:y有多行多列,每一行代表一个数据。计算第一行时,n先取0表示第一行,k=1,2,3,…来求解第一行相应的和;之后n=1表示第二行,k=1,2,3,…计算第二行相应的和;以此类推得到每一行的和,再将上一步求解的每一行的和全部相加,最后除于数据量N,再取负号得到平均每个数据对应的误差。
在这里插入图片描述

(2)、监督数据采用one-hot表示
矩阵

      根据交叉熵误差公式,t矩阵与y矩阵做运算时,t中为0的位置计算之后仍然为0,t中只有为1的位置才是有效计算值。
重点:t矩阵中元素为1的位置对应的下标索引就表示该行对应的图片的正确数字是多少。比如第一行表示正确数字是1, 第二行表示正确数字是3,第三行表示正确数字是0。也就是说只有t中正确数字所对应位置才会用来计算损失函数的值。也就是y中只有(0,1),(1,3),(2,0)位置对应的数据才用来计算误差。

实现代码:

    def cross_entropy_error_one_hot(self, y, t):
        # 监督数据采用one-hot表示
        if y.ndim == 1:
            y = y.reshape(1, y.size)
            t = t.reshape(1, t.size)
        batch_size = y.shape[0]
        return -np.sum(t * np.log(y + 1e-7)) / batch_size

(3)、监督数据采用非one-hot表示
采用上面的例子,y不变,t形式改变了,t里面存储的就是正确的数字,即t=[1, 3, 0]
这个先来代码,再解释为啥子这么写嘛。

def cross_entropy_error(y, t):
    if y.ndim == 1:
        # 变成二维
        y = y.reshape(1, y.size)
        t = t.reshape(1, t.size)
    batch_size = y.shape[0]
    return - np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

解释第一步:

y[np.arange(batch_size), t]

假设batch_size=3,这里np.arange(batch_size)会生成数组[0,1 ,2]。之后[np.arange(batch_size), t]会将[0,1,2]t中正确的解数字[1,3,0]进行对应组合,得到坐标(0,1),(1,3),(2,0)。最后用这些坐标到y矩阵里获取对应位置的元素值,比如y(0,1)取第一行第二列的值0.90。所以这行代码会生成numpy数组[0.90,0.01,0.90]。之后用这个数组取对数,求和,除于数据量,取负号。
总结:殊途同归,最后都是用[0.90,0.01,0.90]计算。采用one-hot时,t里面的1对应的位置提供了行坐标和列坐标去取y里面的值;采用非one-hot时,np.arange(batch_size)提供了行坐标,t提供列坐标,组合之后去取y里面的值。所取到的数据都是一样的。

(4)、合并两个代码
    def cross_entropy_error(self, y, t):
        """
        通用的交叉熵误差函数
        思想:将one-hot表示的t变为非one-hot表示
        """
        if y.ndim == 1:
            # 变成二维
            y = y.reshape(1, y.size)
            t = t.reshape(1, t.size)

        # y.size会求解y里面总共有多少个元素
        # if成立,说明t使用one-hot形式表示,需要变为非one-hot表示
        if y.size == t.size:
            t = t.argmax(axis=1)  # 返回一个由每一行的最大值对应的下标索引组成的numpy数组
        
        batch_size = y.shape[0]
        return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size
二、源代码
# coding:utf-8
import os, sys
sys.path.append(os.pardir)
from dataset.mnist import load_mnist
import numpy as np
from common.functions import softmax


class simpleNet(object):
    """
    只要一层的网络
    """
    def __init__(self):
        # 初始化权重和偏置
        self.w = np.random.randn(784,10)  # 高斯分布来初始化权重
        self.b = np.zeros(10)  # 偏置全部设置为0

    def predict(self, x):
        y = np.dot(x, self.w)
        y += self.b
        return y

    def cross_entropy_error_one_hot(self, y, t):
        # 监督数据采用one-hot表示
        if y.ndim == 1:
            y = y.reshape(1, y.size)
            t = t.reshape(1, t.size)
        batch_size = y.shape[0]
        return -np.sum(t * np.log(y + 1e-7)) / batch_size

    def cross_entropy_error_not_one_hot(self, y, t):
        print("in cross_entropy_error_not_one_hot: ")
        if y.ndim == 1:
            y = y.reshape(1, y.size)
            t = t.reshape(1, t.size)

        # 先观察y和t的形状
        print("shape of y: ", y.shape)
        print("shape of t: ", t.shape)

        batch_size = y.shape[0]  # 一次批处理的数据量
        print("batch_size:" ,batch_size)

        # return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

        # 1. 处理y[np.arange(batch_size), t]
        tmp = y[np.arange(batch_size), t]
        print("t: ", t, "\n")
        print("y: ", y, "\n")
        print("tmp: ", tmp)

        # 2. 取对数
        tmp2 = np.log(tmp)
        print("\ntmp2:", tmp2)

        # 3. 求和在除数据量,可以得到平均单个数据的损失
        loss = -np.sum(tmp2) / batch_size
        print("\nloss: ", loss)

        return loss

    def cross_entropy_error(self, y, t):
        """
        通用的交叉熵误差函数
        思想:将one-hot表示的t变为非one-hot表示
        """
        if y.ndim == 1:
            # 变成二维
            y = y.reshape(1, y.size)
            t = t.reshape(1, t.size)

        # y.size会求解y里面总共有多少个元素
        # if成立,说明t使用one-hot形式表示,需要变为非one-hot表示
        if y.size == t.size:
            t = t.argmax(axis=1)  # 返回一个由每一行的最大值对应的下标索引组成的numpy数组

        batch_size = y.shape[0]
        return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

if __name__ == "__main__":
    # 监督数据(正确解标签)label_train和label_test采用非one-hot表示
    (img_train, label_train), (img_test, label_test) = load_mnist(normalize=True, one_hot_label=False)
    # print("label_train: ", label_train)  # label_train的值就是正确解对应的数字
    net = simpleNet()

    # 随机选择数据
    train_size = img_train.shape[0]
    batch_size = 10  # 选择10个数据
    batch_mask = np.random.choice(train_size, batch_size)
    img_train = img_train[batch_mask]
    t = label_train[batch_mask]

    # 训练数据正向传播
    y = net.predict(img_train)
    y = softmax(y)  # 通过网络正向传播得到的y值

    print("in main: ")
    print(y.shape)
    print(t.shape)
    print('-'*40, '\n')
    loss = net.cross_entropy_error_not_one_hot(y, t)

运行结果分析:
在这里插入图片描述

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值