27 Feb 2018 |
deep-learning |
- 权重:一个较大的权重意味着神经网络认为这个输入比其它输入更重要,较小的权重意味着数据不是那么重要。
- 偏置:更多可能性返回某一个结果(或者说更倾向于某种结果)
- 激活函数:将求和的结果转换成某种输出信号
- 误差 (error):一个普遍的指标是误差平方和 sum of the squared errors (SSE)
SSE的公式如下:
SSE的公式中,使用平方而不使用绝对值的原因是:平方后误差大的得到的惩罚也越大,平方后误差小的得到的惩罚也越小;平方也可以简化后续的计算。1/2 也是为了简化后续步骤计算。
下图是简化的图形:
(我们的目的就是得到最小值时的权重值)
从图中我们知道:
- 误差只与权重有关,通过一步步改变权重,可以得到误差的最小值。
- 权重的更新与梯度的方向恰好相反。
- 学习速率用来控制梯度下降中更新步长的大小。
梯度下降的代码实现
#现在假设只有一个输出单元,来把这个写成代码。我们还是用 sigmoid 来作为激活函数 f(h)。
# Defining the sigmoid function for activations
# 定义 sigmoid 激活函数
def sigmoid(x):
return 1/(1+np.exp(-x))
# Derivative of the sigmoid function
# 激活函数的导数
def sigmoid_prime(x):
return sigmoid(x) * (1 - sigmoid(x))
# Input data
# 输入数据
x = np.array([0.1, 0.3])
# Target
# 目标
y = 0.2
# Input to output weights
# 输入到输出的权重
weights = np.array([-0.8, 0.5])
# The learning rate, eta in the weight step equation
# 权重更新的学习率
learnrate = 0.5
# the linear combination performed by the node (h in f(h) and f'(h))
# 输入和权重的线性组合
h = x[0]*weights[0] + x[1]*weights[1]
# or h = np.dot(x, weights)
# The neural network output (y-hat)
# 神经网络输出
nn_output = sigmoid(h)
# output error (y - y-hat)
# 输出误差
error = y - nn_output
# output gradient (f'(h))
# 输出梯度
output_grad = sigmoid_prime(h)
# error term (lowercase delta)
error_term = error * output_grad
# Gradient descent step
# 梯度下降一步
del_w = [ learnrate * error_term * x[0],
learnrate * error_term * x[1]]
# or del_w = learnrate * error_term * x
27 Feb 2018 |
deep-learning |
从这个例子中你可以看到 sigmoid 做激活函数的一个缺点。sigmoid 函数导数的最大值是 0.25,因此输出层的误差被减少了至少 75%(1-0.25),隐藏层的误差被减少了至少 93.75%(1 - 0.25 * 0.25)!如果你的神经网络有很多层,使用 sigmoid 激活函数会很快把靠近输入层的权重步长降为很小的值,该问题称作梯度消失。
反向传播实例 1
import numpy as np
def sigmoid(x):
"""
Calculate sigmoid
"""
return 1 / (1 + np.exp(-x))
def sigmoid_prim(x):
"""
Calculate sigmoid
"""
return sigmoid(x) * (1 - sigmoid(x))
x = np.array([0.5, 0.1, -0.2])
target = 0.6
learnrate = 0.5
weights_input_hidden = np.array([[0.5, -0.6],
[0.1, -0.2],
[0.1, 0.7]])
weights_hidden_output = np.array([0.1, -0.3])
## Forward pass
hidden_layer_input = np.dot(x, weights_input_hidden)
hidden_layer_output = sigmoid(hidden_layer_input)
output_layer_in = np.dot(hidden_layer_output, weights_hidden_output)
output = sigmoid(output_layer_in)
## Backwards pass
## TODO: Calculate output error
error = target - output
# TODO: Calculate error term for output layer
output_error_term = error * sigmoid_prim(output_layer_in)
print(output_error_term)
print(weights_hidden_output)
# TODO: Calculate error term for hidden layer
hidden_error = np.dot(output_error_term, weights_hidden_output)
hidden_error_term = hidden_error * sigmoid_prim(hidden_layer_input)
print("hidden_error_term:" ,hidden_error_term)
print(" ",x[:,None])
# TODO: Calculate change in weights for hidden layer to output layer
delta_w_h_o = learnrate * output_error_term * hidden_layer_output
print("delta_w_h_o:",delta_w_h_o)
# TODO: Calculate change in weights for input layer to hidden layer
delta_w_i_h = learnrate * x[:,None] * hidden_error_term
print('Change in weights for hidden layer to output layer:')
print(delta_w_h_o)
print('Change in weights for input layer to hidden layer:')
print(delta_w_i_h)
反向传播实例 2
import numpy as np
from data_prep import features, targets, features_test, targets_test #data import
np.random.seed(21)
def sigmoid(x):
"""
Calculate sigmoid
"""
return 1 / (1 + np.exp(-x))
def sigmoid_prime(x):
"""
Calculate sigmoid
"""
return sigmoid(x) * (1 - sigmoid(x))
# Hyperparameters
n_hidden = 2 # number of hidden units
epochs = 900
learnrate = 0.005
n_records, n_features = features.shape
last_loss = None
# Initialize weights
weights_input_hidden = np.random.normal(scale=1 / n_features ** .5,
size=(n_features, n_hidden))
weights_hidden_output = np.random.normal(scale=1 / n_features ** .5,
size=n_hidden)
for e in range(epochs):
del_w_input_hidden = np.zeros(weights_input_hidden.shape)
del_w_hidden_output = np.zeros(weights_hidden_output.shape)
for x, y in zip(features.values, targets):
## Forward pass ##
# TODO: Calculate the output
hidden_input = np.dot(x,weights_input_hidden)
hidden_output = sigmoid(hidden_input)
o_input = np.dot(hidden_output,weights_hidden_output)
output = sigmoid(o_input)
## Backward pass ##
# TODO: Calculate the network's prediction error
error = y - output
# TODO: Calculate error term for the output unit
output_error_term = error * sigmoid_prime(o_input)
## propagate errors to hidden layer
# TODO: Calculate the hidden layer's contribution to the error
hidden_error = np.dot(output_error_term , weights_hidden_output)
# TODO: Calculate the error term for the hidden layer
hidden_error_term = hidden_error * sigmoid_prime(hidden_input)
# TODO: Update the change in weights
del_w_hidden_output += output_error_term * hidden_output
del_w_input_hidden += hidden_error_term * x[:,None]
# TODO: Update weights
weights_input_hidden += learnrate * del_w_input_hidden / n_records
weights_hidden_output += learnrate * del_w_hidden_output / n_records
# Printing out the mean square error on the training set
if e % (epochs / 10) == 0:
hidden_output = sigmoid(np.dot(x, weights_input_hidden))
out = sigmoid(np.dot(hidden_output,
weights_hidden_output))
loss = np.mean((out - targets) ** 2)
if last_loss and last_loss < loss:
print("Train loss: ", loss, " WARNING - Loss Increasing")
else:
print("Train loss: ", loss)
last_loss = loss
# Calculate accuracy on test data
hidden = sigmoid(np.dot(features_test, weights_input_hidden))
out = sigmoid(np.dot(hidden, weights_hidden_output))
predictions = out > 0.5
accuracy = np.mean(predictions == targets_test)
print("Prediction accuracy: {:.3f}".format(accuracy))
25 Feb 2018 |
deep-learning |
Python 很方便,但也会很慢。不过,它允许你访问执行用 C 等语言编写的代码的库。NumPy 就是这样一个库:它为 Python 中的数学运算提供了一个更快速的替代方案,可以与数字组高效搭配使用 - 如矩阵。
数据类型
NumPy可以存储任意维度的数据类型:标量(0维)、向量(1维)、矩阵(2维)、张量(多维)。
>>> import numpy as np # 导入numpy命名为np
>>> s = np.array(3) #创建零维标量
>>> s
array(3)
>>> s.shape
() # 0维
>>> s = np.array([1, 2, 3]) # 创建一维向量
>>> s
array([1, 2, 3])
>>> s.shape
(3,) # 1维
>>> s = np.array([[1,1], [2, 2]]) # 创建二维矩阵
>>> s
array([[1, 1],
[2, 2]])
>>> s.shape
(2, 2) # 2 维
>>> s = np.array([[[1,1], [2,2]],[[3,3], [4,4]] ]) #创建三维张量
>>> s
array([[[1, 1],
[2, 2]],
[[3, 3],
[4, 4]]])
>>> s.shape
(2, 2, 2) # 3 维
>>>
更改形状
有时,你需要更改数据的形状,而无需实际更改其内容。例如,你可能有一个一维的向量,但是需要一个二维的矩阵。
>>> v = np.array([1, 2, 3]) # 创建一维向量
>>> v.shape
(3,) # 1维
>>> v
array([1, 2, 3])
>>> x = v.reshape(1, 3) #方式1: 更改形状为 1 x 3 的二维矩阵
>>> x.shape
(1, 3)# 2维
>>> x
array([[1, 2, 3]])
>>> x = v[None, :] #方式2: 更改形状为 1 x 3 的二维矩阵
>>> x
array([[1, 2, 3]])
>>> x.shape
(1, 3)# 2维
>>> x = v.reshape(3,1) #方式1: 更改形状为 3 x 1 的二维矩阵
>>> x.shape
(3, 1)# 2 维
>>> x
array([[1],
[2],
[3]])
>>> x = v[:, None] #方式2: 更改形状为 3 x 1 的二维矩阵
>>> x.shape
(3, 1)# 2 维
>>> x
array([[1],
[2],
[3]])
>>>
x = v[None, :]
或者x = v[:, None]
将会创建一个切片,查看 v 的所有项目,要求 NumPy 为相关轴添加大小为 1 的新维度(在None的地方增加一个维度)。
>>> n = np.array([[1,2],[3,4]]) #创建一个二维矩阵
>>> n.shape
(2, 2)# 2 维
>>> n
array([[1, 2],
[3, 4]])
>>> z = n[:, :, None] #增加一维变为三维
>>> z
array([[[1],
[2]],
[[3],
[4]]])
>>> z.shape
(2, 2, 1) # 3维
>>> z = n[None, :, :] #增加一维变为三维
>>> z
array([[[1, 2],
[3, 4]]])
>>> z.shape
(1, 2, 2) # 3维
>>> z = n[:, None, :] #增加一维变为三维
>>> z
array([[[1, 2]],
[[3, 4]]])
>>> z.shape
(2, 1, 2) # 3维
>>>
NumPy 矩阵乘法
元素级乘法
你可以使用 multiply 函数或 * 运算符来实现。回顾一下,它看起来是这样的:
m = np.array([[1,2,3],[4,5,6]])
m
# 显示以下结果:
# array([[1, 2, 3],
# [4, 5, 6]])
n = m * 0.25
n
# 显示以下结果:
# array([[ 0.25, 0.5 , 0.75],
# [ 1. , 1.25, 1.5 ]])
m * n
# 显示以下结果:
# array([[ 0.25, 1. , 2.25],
# [ 4. , 6.25, 9. ]])
np.multiply(m, n) # 相当于 m * n
# 显示以下结果:
# array([[ 0.25, 1. , 2.25],
# [ 4. , 6.25, 9. ]])
矩阵乘积
要获得矩阵乘积,你可以使用 NumPy 的 matmul 函数。
如果你有兼容的形状,那就像这样简单:
a = np.array([[1,2,3,4],[5,6,7,8]])
a
# 显示以下结果:
# array([[1, 2, 3, 4],
# [5, 6, 7, 8]])
a.shape
# 显示以下结果:
# (2, 4)
b = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
b
# 显示以下结果:
# array([[ 1, 2, 3],
# [ 4, 5, 6],
# [ 7, 8, 9],
# [10, 11, 12]])
b.shape
# 显示以下结果:
# (4, 3)
c = np.matmul(a, b)
c
# 显示以下结果:
# array([[ 70, 80, 90],
# [158, 184, 210]])
c.shape
# 显示以下结果:
# (2, 3)
如果你的矩阵具有不兼容的形状,则会出现以下错误:
np.matmul(b, a)
# 显示以下错误:
# ValueError: shapes (4,3) and (2,4) not aligned: 3 (dim 1) != 2 (dim 0)
NumPy 的 dot 函数
有时候,在你以为要用 matmul 函数的地方,你可能会看到 NumPy 的 dot 函数。事实证明,如果矩阵是二维的,那么 dot 和 matmul 函数的结果是相同的。
所以这两个结果是等价的:
a = np.array([[1,2],[3,4]])
a
# 显示以下结果:
# array([[1, 2],
# [3, 4]])
np.dot(a,a)
# 显示以下结果:
# array([[ 7, 10],
# [15, 22]])
a.dot(a) # you can call你可以直接对 `ndarray` 调用 `dot`
# 显示以下结果:
# array([[ 7, 10],
# [15, 22]])
np.matmul(a,a)
# array([[ 7, 10],
# [15, 22]])
虽然这两个函数对于二维数据返回相同的结果,但在用于其他数据形状时,你应该谨慎选择。
转置
在 NumPy 中获得矩阵的转置非常容易。只需访问其 T 属性即可。
NumPy 在进行转置时不会实际移动内存中的任何数据 - 只是改变对原始矩阵的索引方式 - 所以是非常高效的。
但是,这也意味着你要特别注意修改对象的方式,因为它们共享相同的数据。例如,对于上面同一个矩阵 m,我们来创建一个新的变量 m_t 来存储 m 的转置。然后看看如果我们修改 m_t 中的值,会发生什么:
m_t = m.T
m_t[3][1] = 200
m_t
# 显示以下结果:
# array([[ 1, 5, 9],
# [ 2, 6, 10],
# [ 3, 7, 11],
# [ 4, 200, 12]])
m
# 显示以下结果:
# array([[ 1, 2, 3, 4],
# [ 5, 6, 7, 200],
# [ 9, 10, 11, 12]])
注意它是如何同时修改转置和原始矩阵的!这是因为它们共享相同的数据副本。所以记住,将转置视为矩阵的不同视图,而不是完全不同的矩阵。