1、神经网络的学习:指的是从训练数据中自动获取最优权重参数的过程。学习的目的是以损失函数为基准,找出能使它的值达到最小的权重参数,采用函数斜率的梯度法可以找出尽可能小的损失函数的值。
2、从数据中学习:神经网络的特征就是从数据中学习,即由数据自动决定权重参数的值。对于线性可分问题,感知机可以利用数据自动学习,根据“感知机收敛定理”,通过有限次数的学习,线性可分问题是可解的,但是非线性可分问题无法通过自动学习解决。
3、数据驱动:数据是机器学习的核心,通常要解决某个问题时,特别是需要发现某种模式时,人们会综合考虑各种因素再给出答案,往往以自己的经验和直觉作为线索,通过反复试验推进工作,而机器学习的方法极力避免人为介入,神经网络或深度学习更是这样,往往尝试从收集到数据中发现答案。
4、识别数字的两种方案:
①先从图像中提取特征量,再用机器学习技术学习这些特征量的模式(部分需要人工参与)。
“特征量”是指可以从输入数据(输入图像)中准确提取本质数据(重要数据)的转换器,图像的特征量通常表示为向量的形式,计算机视觉领域常用的特征量包括SIFT、SURF和HOG等,先使用这些特征量将图像数据转换为向量(需要针对不同问题人工考虑合适的特征量),然后对转换后的向量使用机器学习中的SVM、KNN等分类器进行学习。
②神经网络直接学习图像本身,连图像中包含的重要特征量也都由机器来学习(完全机器学习)。
优点:对所有问题都可以用同样的流程来解决,与待处理问题无关,神经网络会通过不断地学习所提供的数据,尝试发现待求解的问题的模式。
5、机器学习中有关数据处理的一些要点:
6、损失函数:神经网络的学习通过某个指标表示现在的状态,然后以该指标为基准,寻找最优权重参数,这个指标称为损失函数,一般用均方误差和交叉熵误差等。损失函数表示的是神经网络性能的“恶劣程度”,即当前的神经网络对监督数据在多大程度上不拟合/多大程度上不一致,如果乘以一个负值,就可以解释为“性能有多好”。
def mean_squared_error(y,t):
return 0.5 * np.sum((y-t)**2)
def cross_entropy_error(y,t):
delta = 1e-7
return -np.sum(t * np.log(y+delta))
7、mini-batch学习:使用训练数据进行学习就是针对训练函数计算损失函数的值,找出使该值尽可能小的参数,计算损失函数时必须将所有训练数据作为对象。如果要求所有训练数据的损失函数的总和,以交叉熵误差为例,可以改写为下式:
其中,假设数据有N个,t(nk)表示第n个数据的第k个元素的值,y(nk)是神经网络的输出,公式主要是把求单个数据的损失函数扩大到N份数据,最后再除以N进行正规化,通过这样平均化可以获得和训练数据量无关的统一指标。神经网络的学习也是从训练数据中选出一批数据(称为mini-batch,小批量),然后对每个mini-batch进行学习,最后作为全体训练数据的近似值。
#mini-batch版交叉熵误差函数
def cross_entropy_error(y,t):
delta = 1e-7
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 + delta)) / batch_size
#return -np.sum(np.log(y[np.arange(batch_size),t] + delta)) / batch_size
#(监督数据是标签形式,即非one_hot表示)
np.arange(batch_size)函数会生成一个0 - batch-size-1的有序数组,所以y[np.arange(batch_size),t]能抽出各个数据的正确解标签对应的神经网络的输出。
8、设定损失函数的原因:在神经网络中,寻找最优参数时,要寻找使损失函数值尽可能小的参数,这时需要计算参数的导数(梯度),然后以该导数为指引逐步更新参数的值。
对权重参数的损失函数求导,表示的是“稍微改变这个权重参数的值,损失函数的值会如何变化”,如果导数值为负,通过使该权重参数向正方向改变可以增大损失函数的值;如果导数值为正,通过使该权重参数向负方向改变可以减小损失函数的值;当导数值为0时,无论权重参数如何变化,损失函数的值都不变。
不使用识别精度作为指标,是因为这样绝大多数地方的导数都会变为0,导致参数无法更新,仅仅微调参数无法改善识别精度,而把损失函数作为指标时,稍微改变参数值就会发生连续性的变化。
9、导数:表示某个瞬间的变化量,用公式表示导数的含义是,x的微小变化(h无限趋于0)将导致函数f(x)的值在多大程度上变化,对应函数在x处的斜率,在计算时往往对应的是(x+h)和x之间(前向差分即f(x+h)-f(x))的斜率,这个差异的出现是因为h不可能无限接近于0,为了减小这个误差,可以使用中心差分(f(x+h)-f(x-h))。在使用中将h设为10e-50这个微小值,往往会产生舍入误差(指因省略小数的精细部分的数值而造成最终计算结果上的误差),实际使用可以改为10^(-4)。
def numerical_diff(f,x):
h = 1e-4
return (f(x+h)-f(x-h)) / (2*h)
10、偏导数:求有多个变量的导数,每次只把其中的某一个变量当成目标变量求偏导,其他变量视作固定的某个值,偏导数和单变量导数一样,都是求某个地方的斜率。
11、梯度:由全部变量的偏导数汇总而成的向量称为梯度。梯度往往会指向各点处的函数值降低的方向,严格讲梯度指示的方向是各点处的函数值减小最多的方向,再由方向导数cos(Θ) x 梯度,表明所有下降方向中,梯度方向下降最多。
def numerical_gradient(f,x):
h = 1e-4 #0.0001
grad = np.zeros_like(x) #生成和x形状相同的数组
for idx in range(x.size):
tmp_val = x[idx]
x[idx] = tmp_val + h #f(x+h)
fxh1 = f(x)
x[idx] = tmp_val - h #f(x-h)
fxh2 = f(x)
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = tmp_val #还原变量值
return grad
12、梯度法:是解决机器学习中最优化问题的常用方法,通过使用梯度来寻找函数最小值或尽可能小的值的方法,可以让神经网络在学习时顺利找到最优参数,实际上无法保证梯度所指方向就是函数最小值或者真正应该前进的方向,但是沿着它的方向能够最大限度地减小函数的值。
在梯度法中,要以梯度的信息为线索,函数的取值从当前位置沿着梯度方向前进一定距离,然后在新的地方重新求梯度,再沿着新梯度方向前进,如此反复,不断沿着梯度方向前进,逐渐减小函数值。根据目的的不同可以分为梯度下降法(寻找最小值)和梯度上升法(寻找最大值),通过反转损失函数的符号可以求解反问题,所以本质上没有区别。
上式用于表示更新一次的结果,这个步骤反复执行会逐渐减小函数值。η表示更新量,在神经网络的学习过程中称为学习率,用来决定在学习过程中应该学习多少以及在多大程度上更新参数,作为一种超参数,通常由人工设定,不同于神经网络的参数可以通过训练数据和学习算法自动获得,如果学习率过大,会发散成一个很大的值,过小的话起不到更新作用。
def gradient_descent(f,init_x,lr=0.01,step_num=100):
x = init_x
for i in range(step_num):
grad = numerical_gradient(f,x)
x -= lr * grad #梯度法公式
return x
其中,f是要进行最优化的函数,Init_x是初始值,lr是学习率learning rate,step_num是梯度法的重复次数。
13、神经网络的梯度:指损失函数关于权重参数的梯度,比如对于一个形状为2x3的权重W的神经网络,损失函数用L表示,梯度可以表示为:
class simpleNet:
def __init__(self):
self.W = np.random.randn(2,3) #用高斯分布初始化
def predict(self,x):
return np.dot(x,self.W)
def loss(self,x,t):
z = self.predict(x)
y = softmax(z)
loss = cross_entropy_error(y,t)
return loss
14、学习算法的实现——随机梯度下降法SGD:
①前提——神经网络存在合适的权重和偏置,调整权重和偏置的过程即为学习过程;
准备工作:_ init _(self,input_size,hidden_size,output_size)是类的初始化方法(生产TwoLayerNet实例时被调用的方法),参数依次表示输入层神经元数、隐藏层神经元数、输出层神经元数,权重要使用符合高斯分布的随机数进行初始化,偏置使用0初始化。
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.randan(input_size, hidden_size)
self.params['b1'] = np.zeros(hidden_size)
self.params['W2'] = weight_init_std * np.random.randan(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): #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
②mini-batch——从训练数据中随机选出一部分数据,目标是减小mini-batch的损失函数的值,在手写数据识别的神经网络中对包含100笔数据的mini-batch求梯度,使用SGD更新参数,更新次数设置为10000,每更新一次都对训练数据计算损失函数的值,并把该值添加到数组中,通过反复浇灌数据,神经网络逐渐向最优参数靠近;
③计算梯度——求出各个权重参数的梯度(损失函数值减小最多的方向);
④更新参数——将权重参数沿着梯度方向进行微小更新;
⑤重复上述过程。
(x_train,t_train),(x_test,t_test) = load_mnist(normalize=True,one_hot_label=True)
train_loss_list = []
#超参数
iters_num = 10000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1
network = TwoLayerNet(input_size=784,hidden_size=50,output_size=10)
for i in range(iters_num):
batch_mask = np.random.choice(train_size,batch_size) #mini-batch
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)
15、基于测试数据的评价:神经网络学习过程中,必须确认是否能够正确识别训练数据之外的其他数据,即确认是否发生过拟合(在训练数据中的数字图像能被正确识别,但是不在训练数据中的数字图像无法被识别),在进行学习的过程中,要定期对训练数据和测试数据记录识别精度,每经过一个epoch(表示学习中所有训练均被使用过一次时的更新次数)都要记录训练数据和测试数据的识别精度。
#加入for循环
if i % iter_per_epoch == 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("train acc|test acc")
print(str(train_acc) + "|" + str(test_acc))