深度学习小结
一.理论部分
1. 神经网络训练过程中,哪些量是已知的,哪些量是未知的?学习的目标到底是什么?
已知量:训练数据集,包括输入特征x和映射对应的“标签”y。
未知量:权重(W)和偏置(b)
学习目标:通过调整未知量,使模型预测的结果与真实标签尽可能一致,为此确定一个最理想的能够描述数据分布的函数关系。
Q2. 为什么单层感知机只能解决线性可分问题?
单层感知机只能处理线性可分问题,这是由它的数学本质决定的,我们可以通过或门的例子来清晰说明:
以或门为例,当权重参数为(b,w1,w2)=(−0.5,1.0,1.0)时,其输出规则为:

这里的−0.5+x1+x2=0是一个线性方程,在二维输入空间中对应一条直线,这条直线就是感知机的决策边界。它把输入空间分成了两个部分。在感知机的结果中,就是一部分0的空间和一部分1的空间。同时,从这个表达式看出来,它的数学本质就是一个线性函数,自然它能处理的就是线性问题。
再者,如果两类样本可以被一个线性超平面完全分开,就称它们是线性可分的。从这一感知机结果的角度看,它也只能处理线性问题。
如果想处理非线性问题,就需要叠加多层感知机(即神经网络)。通过叠加多层感知机,可以组合出复杂的非线性决策边界,从而解决更复杂的问题。
Q3. 为什么必须引入非线性激活函数? 如果把神经网络中所有激活函数都去掉,会发生什么?
在神经网络中使用非线性激活函数,其目的在于保证神经网络的普遍适用性,使其可以任意拟合各种函数。如果使用线性激活函数,那么就只能拟合线性函数情况,不适用复杂情况。为了更深刻理解,我们可以举个例子:
我们可以设置一个三层神经网络:
我们经过数学计算可以推导出y的值:
这还是关于x1和x2的一个线性函数,本质上我们可以通过改变权重和偏置来使隐藏层消失,无论我们堆叠多少层,它依旧是一个单层感知机。就像我们在上一道题目中得出来的结论,它还是只能处理线性问题,而现实世界中大多数系统则是非线性的,所以,为了普适性,我们使用的是非线性激活函数。
Q4. 神经网络的前向传播,本质上在做什么数学运算?
输入层到隐藏层:线性:矩阵乘法和加法
非线性:非线性函数变换
隐藏层到隐藏层: 线性:矩阵乘法和加法
非线性:非线性函数变换
隐藏层到输出层:线性:矩阵乘法和加法,如果是回归任务就是恒等函数变换
非线性:如果是分类问题就是softmax
Q5. 为什么分类问题中,输出层通常使用 Softmax?
Softmax 在概率意义上做了什么?


从上面这个过程看,softmax回归完全可以看为一个具有多个输出的单层神经网络,这使得它可以同时识别所有类别,因此它别广泛用在分类问题上,而且它并不会改变所有类别间的大小关系,它只会为每个类别分配相应的概率。
神经网络最后一层算出来的数,它是无规律的,可能正可能负可能特别大可能特别小,通过softmax函数,它会将这些数变成一个在零到一闭区间的正数,为后续统计概率做铺垫。
概率统计的核心规则是,所有可能的结果的概率和为1,而softmax函数具有归一化的特征,Softmax第二步会把第一步得到的所有正数加起来,再让每个正数除以这个总和,加起来总和就是1,符合概率的基本规则。
最后,从数学公式上看,softmax的数学表达式中有指数,它会让结果间的差异更大,是统计后的结果更清晰。
Q6. 为什么“准确率”不能作为训练时的优化目标? 为什么必须引入损失函数?
首先,从数学的角度看,准确率可以用下列这个式子表示:

这显然是一个阶跃函数,只有当参数调整变化到足以改变某个样本的预测类别时,准确率才会发生阶跃式的跳跃,而只对参数作微小调整时候,只要预测的类别没有改变,准确率的数值就完全不变,并且,最重要的一点,这种函数在绝大位置处的导数都是0.
神经网络的训练依赖梯度下降法,其中梯度的计算需要目标函数是连续可导的。如果用准确率为目标,导数几乎处处为零,学习过程几乎无法进行。
而损失函数(如均方误差、交叉熵误差)是连续可导的光滑函数:即使是参数的微小调整没有改变预测类别,也会改变预测概率,从而导致损失值发生微小变化,最后,因为这种变化是可导的,能提供非零的梯度,从而引导参数持续优化。
Q7. 梯度在几何意义上代表什么?为什么沿着负梯度方向更新参数?
由全部变量的偏导数汇总 而成的向量称为梯度(gradient)。
梯度的几何意义:方向:梯度的方向是函数在该点函数值增长最快的方向。
大小:梯度的模长(长度)代表了函数在该方向上的增长速率。
神经网络训练的目标是最小化损失函数,即找到函数的最小值点。梯度是函数值上升最快的方向,那么它的反方向就是函数值下降最快的方向。所以沿着负梯度方向更新参数,能以最快的速度降低损失值,是寻找函数最小值的最高效方法。
Q8. 学习率在梯度下降中起什么作用? 学习率过大会怎样? 过小又会怎样?
学习率决定在一次学习中,应该学习多少,以及在多大程度上更新参数。它平衡了 “优化速度” 和 “收敛精度”,是训练神经网络的关键超参数。
学习率过大的话,参数更新会在最小值点附近剧烈震荡,甚至直接越过最小值,导致损失值越来越大,最终发散到无穷大。
学习率过小的话,参数更新幅度微乎其微,即使迭代很多次,也几乎停留在初始位置左右,无法有效接近最小值。
二.手写神经网络与手写数字识别(实践)
任务 1:激活函数 使用 numpy 实现:Sigmoid,ReLU,Softmax
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
return 1/(1+np.exp(-x))
def relu(x):
return np.maximum(0, x)
def softmax(a):
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
接下来我们看具体图像:
# 绘制激活函数图像
x=np.linspace(-10, 10, 1000)
y_sigmoid=sigmoid(x)
y_relu=relu(x)
plt.figure(figsize=(12, 5))
# Sigmoid
plt.subplot(1, 2, 1)
plt.plot(x, y_sigmoid,linewidth=2.5,label='Sigmoid')
plt.title('Sigmoid激活函数')
plt.xlabel('x')
plt.ylabel('Sigmoid(x)')
plt.grid(True, alpha=0.3)
plt.legend()
# ReLU
plt.subplot(1, 2, 2)
plt.plot(x, y_relu,linewidth=2.5,label='ReLU')
plt.title('ReLU激活函数')
plt.xlabel('x')
plt.ylabel('ReLU(x)')
plt.grid(True,alpha=0.3)
plt.legend()
plt.tight_layout()
plt.show()
#softmax
z = np.array([[2.0, 1.0, 0.1, -1.0, 0.5, 3.0, 1.5, -0.5, 0.8, 1.2]])
# 计算Softmax概率
y = np.exp(z) / np.sum(np.exp(z), axis=1, keepdims=True)
# 绘制Softmax概率分布柱状图
plt.figure(figsize=(10, 5))
categories = [str(i) for i in range(10)] # 数字0-9
plt.bar(categories, y[0],alpha=0.7)
plt.title('Softmax输出:手写数字0-9的概率分布', fontsize=12)
plt.xlabel('数字类别',fontsize=10)
plt.ylabel('Softmax概率(和为1)',fontsize=10)
plt.ylim(0, 1)
plt.grid(axis='y',alpha=0.3)
# 标注每个类别的概率值
for i, prob in enumerate(y[0]):
plt.text(i, prob+0.02, f'{prob:.3f}',ha='center',fontsize=9)
plt.show()



任务 2:损失函数 实现:交叉熵损失(支持 batch 输入)
#任务2:损失函数实现
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(t*np.log(y+1e-7))/batch_size
下面我们可以给一个具体的案例来说明:
# 模拟batch_size=3的预测结果
y = np.array([[0.1, 0.8, 0.1],[0.3, 0.1, 0.6],[0.9, 0.05, 0.05]])
# 对应的真实标签
t = np.array([[0, 1, 0],[0, 0, 1],[1, 0, 0]])
# 计算batch的交叉熵损失
loss1=cross_entropy_error(y,t)
print(f"预测概率:{y}")
print(f"真实One-hot标签:{t}")
print(f"Batch损失值:{loss1:.4f}\n")

任务 3:数值梯度
#任务3:数值梯度
def numerical_gradient(f, x):
h=1e-4 # 0.0001
grad=np.zeros_like(x)
for idx in range(x.size):
tmp_val=x[idx]
# f(x+h)的计算
x[idx]=tmp_val+h
fxh1=f(x)
# f(x-h)的计算
x[idx]=tmp_val-h
fxh2=f(x)
grad[idx]=(fxh1-fxh2)/(2*h)
x[idx]=tmp_val
return grad
#以上适用于一维,下面给出多维
def numerical_gradient(f, x):
h = 1e-4
grad = np.zeros_like(x)
it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
while not it.finished:
idx = it.multi_index
tmp_val = x[idx]
x[idx] = tmp_val + h
fxh1 = f(x)
x[idx] = tmp_val - h
fxh2 = f(x)
grad[idx] = (fxh1 - fxh2) / (2 * h)
x[idx] = tmp_val
it.iternext()
return grad
任务 4:实现一个神经网络
#任务4:实现一个神经网络
class TwoLayerNet:
def __init__(self,input_size,hidden_size,output_size,weight_init_std=0.01):
self.params={}
self.params['W1']=weight_init_std*np.random.randn(input_size,hidden_size)
self.params['b1']=np.zeros(hidden_size)
self.params['W2']=weight_init_std*np.random.randn(hidden_size,output_size)
self.params['b2']=np.zeros(output_size)
def predict(self,x):
W1, W2=self.params['W1'],self.params['W2']
b1, b2=self.params['b1'],self.params['b2']
a1=np.dot(x, W1) + b1
z1=sigmoid(a1)
a2=np.dot(z1, W2) + b2
y=softmax(a2)
return y
def loss(self,x,t):
y=self.predict(x)
return cross_entropy_error(y,t)
def accuracy(self,x,t):
y=self.predict(x)
y=np.argmax(y,axis=1)
t=np.argmax(t,axis=1)
accuracy=np.sum(y==t)/float(x.shape[0])
return accuracy
def numerical_gradient(self,x,t):
loss_W=lambda W: self.loss(x,t)
grads={}
grads['W1']=numerical_gradient(loss_W,self.params['W1'])
grads['b1']=numerical_gradient(loss_W,self.params['b1'])
grads['W2']=numerical_gradient(loss_W,self.params['W2'])
grads['b2']=numerical_gradient(loss_W,self.params['b2'])
return grads
任务 5:模型训练
import sys
sys.path.append("D:/桌面/deep-learning-from-scratch-master")
from dataset.mnist import load_mnist
#任务5:模型训练
# 加载数据集
(x_train,t_train), (x_test,t_test)=load_mnist(normalize=True,one_hot_label=True)
network=TwoLayerNet(input_size=784,hidden_size=50,output_size=10)
iters_num=20
train_size=x_train.shape[0]
batch_size=10
learning_rate=0.1
train_loss_list=[]
train_acc_list=[]
test_acc_list=[]
iter_per_epoch=max(train_size/batch_size,1)
for i in range(iters_num):
batch_mask=np.random.choice(train_size,batch_size)
x_batch=x_train[batch_mask]
t_batch=t_train[batch_mask]
grad=network.numerical_gradient(x_batch,t_batch)
for key in('W1','b1','W2','b2'):
network.params[key]-=learning_rate*grad[key]
loss=network.loss(x_batch,t_batch)
train_loss_list.append(loss)
if i % 2 == 0 and len(train_loss_list) > 0:
plt.plot(train_loss_list)
plt.show()
if i % 5==0:
train_acc=network.accuracy(x_train,t_train)
test_acc=network.accuracy(x_test,t_test)
train_acc_list.append(train_acc)
test_acc_list.append(test_acc)
print(f"train acc, test acc|{train_acc:.4f},{test_acc:.4f}")
# 绘制损失函数变化
plt.plot(train_loss_list)
plt.xlabel("Iteration")
plt.ylabel("Loss")
plt.show()
# 绘制准确率变化
plt.plot(train_acc_list,label='train acc')
plt.plot(test_acc_list,label='test acc')
plt.xlabel("Epoch")
plt.ylabel("Accuracy")
plt.legend()
plt.show()



为了看到即时结果,使用了较小数字。
更多推荐

所有评论(0)