【引言】
深度学习就像一个黑箱子,在了解了基本的tensorflow框架后,将已经经过预处理的图像输入tf就可以给出预测的结果,但终究只是停留在表面功夫,若想了解深度学习、神经网络相关的tf框架中的工作原理并有所深入了解,那我们还需把箱子打开一探究竟。正如我在【西瓜书笔记】——神经网络中所记录到的,从感知机开始到前馈神经网络再到BP神经网络,这无非是神经网络中最基本的3个知识点。深度神经网络就是在此基础上增加了隐层的个数,复杂的神经网络结构中包含了许多激活函数和损失函数。
框架无非也是建立在以上几个内容的基础上实现的,虽然目前就业的大环境是只要你能够熟练地利用框架就可以找到一份适合的工作,但是多学一些也不见得是坏事。实现的过程看似简单,但实现起来却极其考验基本功,例如怎么写前向传播和BP网络,权值如何迭代更新,损失函数如何选择,激活函数如何写。这些都是博主在此需要一点一点记录的。本专栏的博客是博主在阅读《西瓜书》、《机器学习实战》、公众号“Python爱好者社区”和公众号:“机器之心”等内容的过程中不断记录、总结得到的,纪录在此,自勉于自己,也希望能够将干货分享给大家。
从零搭建神经网络,我们需要:
1、定义网络结构(指定输出层、隐藏层、输出层的大小)
2、初始化模型参数
3、循环以下步骤:执行前馈神经网络 → 计算损失函数 → 执行BP算法 → 更新权值
程序清单
neural-network.py
# -*- coding: utf-8 -*-
"""
Created on Mon Oct 22 05:08:43 2018
@author: Miku
从零开始学深度学习
1、用numpy搭建一个感知机模型
步骤:
定义输入层、隐层、输出层神经元个数
前向传播、激活函数、反向传播、梯度下降、损失计算、权值更新
"""
import numpy as np
#激活函数的类型有很多,这里采用sigmoid函数
#sigmoid函数构造
def sigmoid(x):
sig = 1 / (1 + np.exp(-x))
return sig
#输入模型初始化
def initialize_with_zeros(dim):
w = np.zeros((dim,1))
b = 0.0
return w, b
#前向传播
def propagate(w, b, X, Y):
m = X.shape[1]
#定义每一个神经元上的输出(激活函数)
activate = sigmoid(np.dot(w.T, X) + b)
#定义损失函数,这里利用的是交叉熵损失公式label*log(predict)+(1-label)*log(1-predict)
cost = -1/m * np.sum(Y * np.log(activate) + (1 - Y) * np.log(1 - activate))
#对w求偏导
dw = np.dot(X, (activate - Y).T)/m
#对b求偏导
db = np.sum(activate - Y) / m
#养成assert的习惯可以避免在运行时出现因维度不同、数据类型不同而产生的异常,assert是一个bool类型判别,若为true则直接跳过,若为false会直接中断程序执行
assert(dw.shape == w.shape)
assert(db.dtype == float)
cost = np.squeeze(cost)
assert(cost.shape == ())
grads = { 'dw': dw,
'db': db
}
return grads, cost
#反向传播
def backward_propagate(w,b,X,Y, num_iterations, alpha, print_cost = False):
#定义一个损失列表容器
cost = []
#定义迭代次数
for i in range(num_iterations):
grad, cost = propagate(w,b,X,Y)
dw = grad['dw']
db = grad['db']
#alpha位迭代步长
w = w - alpha * dw
b = b - alpha * db
if i % 100 == 0:
cost.append(cost)
if print_cost and i%100 ==0:
print("cost after iteration %i: %f" %(i, cost))
params = {"dw": w,
"db": b
}
grads = {"dw": dw,
"db": db
}
return params, grads, cost
#预测函数
def predict(w, b, X):
m = X.shape[1]
Y_prediction = np.zeros((1,m))
w = w.reshape(X.shape[0], 1)
activate = sigmoid(np.dot(w.T, X)+b)
for i in range(activate.shape[1]):
if activate[:,i] > 0.5:
Y_prediction[:, i] = 1
else:
Y_prediction[:,i] = 0
assert(Y_prediction.shape == (1,m))
return Y_prediction
#代码封装
def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5, print_cost = False):
# initialize parameters with zeros (≈ 1 line of code)
w, b = initialize_with_zeros(X_train.shape[0])
# Gradient descent (≈ 1 line of code)
parameters, grads, costs = backward_propagate(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost) # Retrieve parameters w and b from dictionary "parameters"
w = parameters["dw"]
b = parameters["db"]
# Predict test/train set examples (≈ 2 lines of code)
Y_prediction_train = predict(w, b, X_train)
Y_prediction_test = predict(w, b, X_test)
# Print train/test Errors
print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))
print("test accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100))
d = {"costs": costs,
"Y_prediction_test": Y_prediction_test,
"Y_prediction_train" : Y_prediction_train,
"w" : w,
"b" : b,
"learning_rate" : learning_rate,
"num_iterations": num_iterations}
return d
显然,这是一个只有1个输入单元和一个输出单元(无隐层)的感知机模型,采用了交叉熵损失来更新权重矩阵和b的值,同时利用自定义sigmoid函数作为激活函数。定义的过程是:
1、定义sigmoid
2、定义前向传播
3、定义反向传播的同时描述梯度下降
4、预测函数
5、代码封装
每一步的参数共享和传递都是相互关联的。以此算法为入门,展开对深度学习的进一步探索。