“深度学习”学习日记。神经网络的学习--损失函数2

2023.1.8

损失函数是表示神经网络模型在使用测试数据时评价其性能的“恶劣程度”的指标,也是在使用训练数据进行学习时,针对训练数据计算损失函数的值,找出使得损失函数的值尽可能小的权重参数。因此,如果损失函数有n个,那么我们也得将这n个损失函数的综合作为学习指标。

以交叉熵函数为例子:

 

 这就好像是单个交叉熵函数扩大到了N分数据,不过最后是除以N,以求的“平均损失函数”,可以获得和训练数据数量无关的统一指标。

当我们遇到上百万的数据时,以全部数据为对象求损失函数的和,耗时很长,也是不科学的。好比MNIST数据集有60000张图片。

所以我们在计算神经网络模型利用MNIST数据集学习权重参数时的损失函数的和的时候,可以从60000个数据中取出1000个,作为60000个数据的“近似”。

神经网络的学习也是从训练数据选出一批数据(mini-batch),然后对“每个”mini-batch进行学习。这样的学习方式也被称为 mini-batch学习 

import numpy as np
import sys, os
from dataset.mnist import load_mnist
import pickle

sys.path.append(os.pardir)

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

print(x_train.shape, '\n', t_train.shape)  # (60000, 784) (60000, 10)
print(x_test.shape, '\n', t_test.shape)

train_size = x_train.shape[0]  # 60000
batch_size = 10
batch_mask = np.random.choice(train_size, batch_size)  # 会在0~60000区间随机选择10个数字作为索引
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]


def loss_function(y, t):
    delta = 1e-7
    return float(-1 / batch_size) * np.sum(-1 * np.sum(t * np.log(y + delta)))


def init_network():
    with open("sample_weight.pkl", 'rb') as f:
        network = pickle.load(f)
    return network


def predict(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']

    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    y = softmax(a3)

    return y


def sigmoid(x):
    return 1 / (1 + np.exp(-x))


def softmax(x):
    if x.ndim == 2:
        x = x.T
        x = x - np.max(x, axis=0)
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        return y.T

    x = x - np.max(x)
    return np.exp(x) / np.sum(np.exp(x))


network = init_network()
y = predict(network, x_batch)
print(loss_function(y, t_batch))

在神经网络的学习阶段(即利用训练数据得出最优权重的阶段)设置损失函数的原因?

损失函数是评价当前神经网络性能的“恶劣程度”的指标,在MNIST识别任务中可以讲损失函数的目的是提高识别精度。与此矛盾的是,为什么我们不能输出精度(正确率)作为评价当前神经网络性能的“恶劣程度”的指标。

如果我们在MNIST识别任务中使用输出精度作为指标,输出精度是0.50;而我们只是微小调整参数权重参数精度可能由0.50000000001变成0.50000000002,或者0.50000000000,在宏观上我们无法感知精度的变化,识别精度仍然保持在0.5左右,即便精度变化成0.51,我们也无法观察到连续变化的过程。

假设我们关注神经网络的某一项参数权重,以损失函数f(x)作为性能指标,并且对损失函数求导得到f'(x),若f'(x)为正时,我们可以将该项参数权重向负方向变化,可以减小f(x)的值,提高神经网络的性能;反之, 若f'(x)为负时,我们可以将该项参数权重向正方向变化,可以减小f(x)的值,提高神经网络的性能;当 f'(x)=0 时,无论参数怎么变化,损失函数的值也不会变化,此时该项权重的更新就会停止。

所以精度对权重参数的微小变化几乎没有反应,即使友反应也不能连续地变化。

出去相同的原因,阶跃函数也不能作为作为激活函数,因为阶跃函数大部分地方的导数都为0,这样即便用损失函数作为指标,参数权重微小的变化也会被抹杀(影响y值)。

 

 也就是说,一个导数不为0的函数,作为激活函数时,也可以很好的发挥损失函数的功能,这里就想到sigmoid函数(斜率恒不为0),得益于这个性质,可以然神经网络模型更好的学习。

 

 下面代码是阶跃函数和sigmoid函数:

import matplotlib.pyplot as plt
import numpy as np


def step_function(x):
    y = np.zeros(len(x))
    for i in range(len(x)):
        if x[i] > 0:
            y[i] = 1

    return y


def sigmoid(x):
    return 1 / (1 + np.exp(-x))


x = np.arange(-10, 10)
plt.plot(x, sigmoid(x), label="sigmoid")
plt.title("sigmoid")
plt.show()

下面代码以观察两种损失函数的运算: 

import numpy as np
import sys, os
from dataset.mnist import load_mnist
import pickle  # pickle是python序列化的一个工具!可以用来把对象来以文件的形式存储起来,用的时候再加载

# pickle模块只能在python中使用,python中的几乎所有的数据类型都可以序列化!但是序列化以后得到的文件人看不懂

sys.path.append(os.pardir)


# 我们导入的x数据是28×28=784的图片
def get_data():
    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
    return x_test, t_test


# normalize= 归一化(正规化)将输入图片归化为0.0~1.0的值
# flatten= 设置是否将图像变成一维数组
# one_hot_label= 表示仅正确解标签1,其余的归化为0


# 现在这个阶段没学习的神经网络是如何学习得到参数的,假设“学习”好了,将学习好的权重参数保存到"sample_weight.pkl"
# 该文件以字典变量的形式保存权重和参数
def init_network():
    with open("sample_weight.pkl", 'rb') as f:  # rb: 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头
        network = pickle.load(f)  # load()函数的作用是反序列化恢复成python对象
    return network


# predict()函数以numpy数组的形式输出各个标签的对应的概率
def predict(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']

    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    y = softmax(a3)  # 输出层设计 分类问题 通过线性代数的运算,得到符合我们需要的10个输出层
    # print(x.shape)  # (784,)
    # print(W1.shape)  # (784, 50)
    # print(W2.shape)  # (50, 100)
    # print(W3.shape)  # (100, 10)
    # print("y的值")
    # print(y, '\n')

    return y


def sigmoid(x):
    return 1 / (1 + np.exp(-x))


def softmax(x):  # 一种神经网络的激活函数
    if x.ndim == 2:  # 判断数组x的维度是否为2
        x = x.T  # 数组(矩阵)x的转置
        x = x - np.max(x, axis=0)
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        return y.T

    x = x - np.max(x)
    return np.exp(x) / np.sum(np.exp(x))


def get_t1():
    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
    return t_test


def mean_squared_error(y, t):
    return 0.5 * np.sum((y - t) ** 2)


def cross_entropy_error(y, t):
    delta = 1e-7
    return -1 * np.sum(t * np.log(y + delta))


x, t = get_data()
t1 = get_t1()
# print(t1)
# print(x.shape, t.shape, t1.shape)
# print("x的值", '\n', x, '\n', "t的值", '\n', t, '\n')
network = init_network()
accuracy_cnt = 0

for i in range(len(x)):
    y = predict(network, x[i])
    t1_batch = t1[i]
    print("神经网络输出结果:", y)
    print("正解的数字标签:", '\n', t1_batch)
    p = np.argmax(y)  # 获取概率最高的元素的索引
    print("均方误差:", mean_squared_error(y, t1_batch))
    print("交叉熵误差:", cross_entropy_error(y, t1_batch), '\n')
    if p == t[i]:
        accuracy_cnt += 1

print("Accuracy:" + str(float(accuracy_cnt) / len(x)))

部分结果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值