avatar

目录
感知器模型

感知器模型

感知器是一种模拟人的神经元的一种算法模型,是一种研究单个训练样本的二元分类器,是SVM和人工神经网络(ANN, Artificial Neural Networks)的基础。
一个感知器接受几个二进制的输入,并产生一个二进制的输出(阶跃函数),通常的表达方式如下:1_ys

感知器可以看作是根据权重来做出决定的一个设备/单元,只要我们可以给定一个比较适合的权重以及阈值,那么感知器应该是能够对数据进行判断的/分类预测的.

感知器神经元–逻辑与(and)

单个神经元完成逻辑与功能:2

感知器神经元直观理解之逻辑或or

单个神经元完成逻辑或功能:3_ys

感知器神经元直观理解之非线性可分

通过将P1与P2结合完成非运算

4_ys

5_ys

神经网络-基本架构

针对感知器网络的这种很难学习的问题(非线性),引入S型神经元来代替感知器,从而解决这个问题。

从感知器模型中,我们可以将单个神经元的计算过程看成下列两个步骤:
先计算权重w和输入值x以及偏置项b之间的线性结果值z:z=wx+b
然后对结果值z进行一个数据的sign函数(变种)转换,得到一个离散的0/1值: y=int((sign(z)+1)/2)

在S型神经元中,和感知器神经元的区别在于:
对于结果值z的转换,采用的不是sign函数进行转换,是采用平滑类型的函数进行转换,让输出的结果值y最终 是一个连续的,S型神经元转指使用的是sigmoid函数。

神经网络来源之“神经元”

输入:x1、x2、x3和截距+1;输出:函数hw,b(x),其中w权重和b偏置项是参数6_ys

函数f被称为“激活函数”;常用/最好出现激活函数有sigmoid(逻辑回归函数)和tanh(双曲正切函数)

7_ys

7_ys

激活函数

激活函数的主要作用是提供网络的非线性建模能力。如果没有激活函数,那么该网络仅能够表达线性映射,此时即便有再多的隐藏层,其整个网络跟单层神经网络也是等价的。因此也可以认为,只有加入了激活函数之后,深度神经网络才具备了分层的非线性映射学习能力。 激活函数的主要特性是:可微性、单调性、输出值的范围;

16_ys

常见的激活函数:Sign函数、Sigmoid函数、Tanh函数、ReLU函数、Leaky-ReLU函数、ELU函数等

神经网络

神经网络主要由三个组成部分,第一个是架构(architecture)或称为拓扑结构(topology),描述神经元的层次与连接神经元的结构。第二个组成部分是神经网络使用的激励/激活函数。第三个组成部分是找出最优权重值的学习算法(SGD,BGD,MBGD)。

神经网络主要分为两种类型:前馈神经网络(Feedforward Neural Networks)是最常用的神经网络类型(如FC CNN),一般定义为有向无环图,信号只能沿着最终输出的那个方向传播。另外一个是反馈神经网络(Feedback Neural Networks),也称为递归神经网络(Recurent Neural Networks),也就是网络中环。

神经网络之浅层神经网络

添加少量隐层的神经网络就叫做浅层神经网络;也叫作传统神经网络,一般为2隐层的神经网络(超过两隐层的话,效果会差很多).

深层网络面临问题:硬件,梯度消散(链式求导导致)

7_ys

9

链式求导:(反向)$ \frac{\partial Loss}{\partial w^0}=\frac{\partial Loss}{\partial \tilde{y}}*w^3 \frac{\partial \tilde{y}}{\partial h_3}*w^2\frac{\partial h_3}{\partial h^2}…$

11_ys

神经网络之非线性可分

对线性分类器的与和或的组合可以完成非线性可分的问题;即通过多层的神经网络中加入激活函数的方式可以解决非线性可分的问题。

神经网络之过拟合

  1. 理论上来讲,单隐层的神经网络可以逼近任何连续函数(只要隐层的神经元个数足够的多<一个神经元将数据集分为两类>)

  2. 虽然从数学表达上来讲,效果一样,但是在网络工程效果中,多隐层的神经网络效果要比单隐层的神经网络效果好

  3. 对于一些分类的问题来讲,三层的神经网络效果优于两层的神经网络,但是如果把层次不断增加(4,5,6,7…),对于最终的效果不会产生太大的变化
    提升隐层层数或者神经元个数,神经网络的“容量”会变大,那么空间表达能力会变强(模型的拟合能力变强),从而有可能导致过拟合的问题(解决:1.droppwt2.正则化3.减小模型复杂度)

  4. 对于视频/图片识别/文本/语音 等问题,传统的神经网络(全连接神经网络)不太适合

反向传播神经网络-BPNN

BP(梯度下降)

梯度下降的数学推导

13_ys

14_ys

反向传播-练习

12_ys

15_ys

![17 (4)_ys](/img/深度学习/17 (4)_ys.png)

![17 (1)_ys](/img/深度学习/17 (1)_ys.png)

20_ys

19_ys

python
SGD:实际应用中用全部样本随机打乱取均值,如果每次在样本中随机去选择 成本很高。
实际工作中大部分用到的是小批量梯度算法

"""
案例:研究生学院录取数据,用梯度下降训练一个网络。
数据有三个输入特征:GRE 分数、GPA 分数和本科院校排名(从 1 到 4)。排名 1 代表最好,排名 4 代表最差。
"""
import numpy as np
import pandas as pd

pd.set_option('display.max_columns', 1000)
pd.set_option('display.width', 1000)
pd.set_option('display.max_colwidth', 1000)

admissions = pd.read_csv('../datas/11.csv')


def data_explore(admissions):
print(admissions.head(n=10))
print(admissions.info()) # 查看是否有空值,以及数据类型
print(admissions.describe()) # 再次可以看到是否有空值,以及值范围,需要考虑做数据变换。
print('各个样本相应的数量为:{}'.format(admissions['admit'].value_counts())) # 查看样本是否均衡


"""
一、数据清理
1、分类变量哑编码
rank 是类别特征,其中的数字并不表示任何相对的值。排名第 2 并不是排名第 1 的两倍;
排名第 3 也不是排名第 2 的 1.5 倍。因此,我们需要用哑变量 来对 rank 进行编码。
把数据分成 4 个新列,用 0 或 1 表示。排名为 1 的行对应 rank_1 列的值为 1 ,其余三列的值为 0;
排名为 2 的行对应 rank_2 列的值为 1 ,其余三列的值为 0,以此类推。
2、连续变量标准化
把 GRE 和 GPA 数据标准化,变成均值为 0,标准偏差为 1。因为 sigmoid 函数会挤压很大或者很小的输入。
很大或者很小输入的梯度为 0,这意味着梯度下降的步长也会是 0。
"""

def data_transform(admissions):
"""
一 rank代表学校等级(1--4),转成哑变量
1、 用pd.get_dummies 将rank列,转成哑变量,新变量名前缀为:prefix='rank'
2、将原数据集admissions 和 1 进行列拼接;
3、drop原始的rank列。
"""
data = pd.concat([admissions, pd.get_dummies(admissions['rank'], prefix='rank')], axis=1)
data = data.drop('rank', axis=1)


"""
二、gre和gpa连续变量的标准化
标准做法:先拆分数据集--使用训练数据集的统计量 去标准化 验证和测试。
"""

for field in ['gre', 'gpa']:
mean, std = data[field].mean(), data[field].std()
data.loc[:, field] = (data[field] - mean) / std #取field所有行

"""
三、数据拆分:分成训练 和 测试数据集
1、设置随机数种子,确保大家执行和我们这里演示的结果一致;
2、使用np.random.choice,随机选择数据集中90% 数据的index
3.iloc,完全基于位置的索引
"""
# 随机打乱,并将数据集拆分为 90%训练---10%测试数据集。
np.random.seed(42)
sample = np.random.choice(data.index, size=int(len(data) * 0.9), replace=False)
train_data, test_data = data.iloc[sample], data.drop(sample)

"""
四、 将自变量(features)和目标值分离
"""
features_train, targets_train = train_data.drop('admit', axis=1), train_data['admit']
features_test, targets_test = test_data.drop('admit', axis=1), test_data['admit']

return features_train.values, targets_train.values, features_test.values, targets_test.values

def sigmoid(x):
"""
sigmoid激活函数
:param x:
:return:
"""
return 1/(1+np.exp(-x))


def gre_bp_answer(feature_train, target_train, feature_test, target_test):
# 1、超参数
n_hidden = 2 #隐藏层节点个数
epochs = 2000 #梯度下降迭代次数
learning_rate = 0.06 #学习率

# 获取样本数量和 特征数量
n_records, n_features = features_train.shape
last_loss = None

# 2、初始化模型权重
weights_input_2_hidden = np.random.normal(
loc=0.0, scale=0.1, size=[n_features, n_hidden]
)
# weights_hidden_2_output = np.random.normal(
# scale=0.1, size=n_hidden
# )
weights_hidden_2_output = np.random.normal(
scale=0.1, size=[n_hidden, 1]
)

# 构建梯度下降迭代次数的循环
for e in range(epochs):
# 构建存储梯度值的delta_w
del_weights_input_2_hidden = np.zeros(weights_input_2_hidden.shape)
del_weights_hidden_2_output = np.zeros(weights_hidden_2_output.shape)
for x, y in zip(feature_train, target_train):
# 1、正向传播;
hidden_input = np.matmul(x, weights_input_2_hidden)
hidden_output = sigmoid(hidden_input)
# hidden_output shape = (2,)

final_input = np.matmul(hidden_output, weights_hidden_2_output)
y_hat = sigmoid(final_input) # shape = () 是一个标量

# 2、求误差
error = y_hat - y
# 3、反向传播
# 求输出层误差项
output_error_term = error * y_hat * (1-y_hat) # 标量
# 隐藏层误差
hidden_error = output_error_term * weights_hidden_2_output # (n_hidden,)
# 隐藏层误差项
# print(hidden_error.shape, hidden_output.shape)
hidden_error_term = hidden_error.reshape(-1) * hidden_output *(1-hidden_output) #reshape 将其拉成一个向量与后面维度保持一致
# (n_hidden,)

del_weights_input_2_hidden += x[:, None] * hidden_error_term #累加求和
del_weights_hidden_2_output += hidden_output[:, None] * output_error_term

# 更新模型权重
weights_input_2_hidden -= del_weights_input_2_hidden * learning_rate / n_records
weights_hidden_2_output -= del_weights_hidden_2_output * learning_rate / n_records

# 打印模型损失
if e % 100 == 0:
hidden_output = sigmoid(np.dot(features_train, weights_input_2_hidden))
pred_out = sigmoid(np.dot(hidden_output, weights_hidden_2_output))
loss = np.mean((pred_out - target_train)**2)

if last_loss and last_loss < loss:
print('警告:模型损失在上升, Train Loss:{}'.format(loss))
else:
print('Epochs:{} - Train Loss:{}'.format(e, loss))
last_loss = loss
#训练结束,使用测试数据集验证模型准确率
hidden = sigmoid(np.dot(feature_test,weights_input_2_hidden))
test_pred = sigmoid(np.dot(hidden,weights_hidden_2_output))
predictions = test_pred > 0.5
accuracy = np.mean(predictions == target_test)
print("Test Accuray:{:,.5f}".format(accuracy))


if __name__ == '__main__':
# data_explore(admissions)
features_train, targets_train, features_test, targets_test = data_transform(admissions)
# print(features_train)
gre_bp_answer(features_train, targets_train, features_test, targets_test)

GRE案例的伪代码(BGD)

传入(train_features, train_target)
n_records, n_features = train_features.shape
hidden_nodes = 3(隐藏层节点数)
learning_rate = 0.01
初始化权重。 Winput2h , Wh2output (随机初始化)
Last_loss = None
epochs = 200(迭代次数)
for e in range(epochs):(所有样本训练一次=一个epochc)
delta_Winput2h, delta_Wh2output = np.zeros(), np.zeros()
for X,y in zip(train_features, train_target):
#1、正向传播;
#2、获得损失(error) 用的MSE作为损失函数
#3、执行反向传播
#4、delta_Winput2h, delta_Wh2output 进行累加
执行Winput2h , Wh2output 梯度下降。
if e % 10==0:
执行正向传播,求出当前的损失,并且打印。
用测试数据集,做正向传播,并且求准确率。

注:zip函数用法

Code
a = [1,2,3]

b = [4,5,6]

c = [4,5,6,7,8]

zipped = zip(a,b) # 打包为元组的列表
[(1, 4), (2, 5), (3, 6)]
>>> zip(a,c) # 元素个数与最短的列表一致 [(1, 4), (2, 5), (3, 6)]
zip(*zipped)
# 与 zip 相反,*zipped 可理解为解压,返回二维矩阵式 [(1, 2, 3), (4, 5, 6)]
Code
小批量梯度下降MBGD 一般选用2的n次方大小


"""
案例:研究生学院录取数据,用梯度下降训练一个网络。
数据有三个输入特征:GRE 分数、GPA 分数和本科院校排名(从 1 到 4)。排名 1 代表最好,排名 4 代表最差。
"""
import numpy as np
import pandas as pd

pd.set_option('display.max_columns', 1000)
pd.set_option('display.width', 1000)
pd.set_option('display.max_colwidth', 1000)

admissions = pd.read_csv('../datas/11.csv')


def data_explore(admissions):
print(admissions.head(n=10))
print(admissions.info()) # 查看是否有空值,以及数据类型
print(admissions.describe()) # 再次可以看到是否有空值,以及值范围,需要考虑做数据变换。
print('各个样本相应的数量为:{}'.format(admissions['admit'].value_counts())) # 查看样本是否均衡


"""
一、数据清理
1、分类变量哑编码
rank 是类别特征,其中的数字并不表示任何相对的值。排名第 2 并不是排名第 1 的两倍;
排名第 3 也不是排名第 2 的 1.5 倍。因此,我们需要用哑变量 来对 rank 进行编码。
把数据分成 4 个新列,用 0 或 1 表示。排名为 1 的行对应 rank_1 列的值为 1 ,其余三列的值为 0;
排名为 2 的行对应 rank_2 列的值为 1 ,其余三列的值为 0,以此类推。
2、连续变量标准化
把 GRE 和 GPA 数据标准化,变成均值为 0,标准偏差为 1。因为 sigmoid 函数会挤压很大或者很小的输入。
很大或者很小输入的梯度为 0,这意味着梯度下降的步长也会是 0。
"""

def data_transform(admissions):
"""
一 rank代表学校等级(1--4),转成哑变量
1、 用pd.get_dummies 将rank列,转成哑变量,新变量名前缀为:prefix='rank'
2、将原数据集admissions 和 1 进行列拼接;
3、drop原始的rank列。
"""
data = pd.concat([admissions, pd.get_dummies(admissions['rank'], prefix='rank')], axis=1)
data = data.drop('rank', axis=1)


"""
二、gre和gpa连续变量的标准化
标准做法:先拆分数据集--使用训练数据集的统计量 去标准化 验证和测试。
"""

for field in ['gre', 'gpa']:
mean, std = data[field].mean(), data[field].std()
data.loc[:, field] = (data[field] - mean) / std

"""
三、数据拆分:分成训练 和 测试数据集
1、设置随机数种子,确保大家执行和我们这里演示的结果一致;
2、使用np.random.choice,随机选择数据集中90% 数据的index
"""
# 随机打乱,并将数据集拆分为 90%训练---10%测试数据集。
np.random.seed(42)
sample = np.random.choice(data.index, size=int(len(data) * 0.9), replace=False)
train_data, test_data = data.iloc[sample], data.drop(sample)

"""
四、 将自变量(features)和目标值分离
"""
features_train, targets_train = train_data.drop('admit', axis=1), train_data['admit']
features_test, targets_test = test_data.drop('admit', axis=1), test_data['admit']

return features_train.values, targets_train.values, features_test.values, targets_test.values

def sigmoid(x):
"""
sigmoid激活函数
:param x:
:return:
"""
return 1/(1+np.exp(-x))


def gre_bp_answer(feature_train, target_train, feature_test, target_test):
# 1、超参数
n_hidden = 2
epochs = 2000
learning_rate = 0.06

# 获取样本数量和 特征数量
n_records, n_features = features_train.shape
last_loss = None

# 2、初始化模型权重
weights_input_2_hidden = np.random.normal(
loc=0.0, scale=0.1, size=[n_features, n_hidden]
)
# weights_hidden_2_output = np.random.normal(
# scale=0.1, size=n_hidden
# )
weights_hidden_2_output = np.random.normal(
scale=0.1, size=[n_hidden, 1]
)

# 构建迭代次数的循环
for e in range(epochs):
# 构建存储梯度值的delta_w
del_weights_input_2_hidden = np.zeros(weights_input_2_hidden.shape)
del_weights_hidden_2_output = np.zeros(weights_hidden_2_output.shape)
for x, y in zip(feature_train, target_train):
# 1、正向传播;
hidden_input = np.matmul(x, weights_input_2_hidden)
hidden_output = sigmoid(hidden_input)
# hidden_output shape = (2,)

final_input = np.matmul(hidden_output, weights_hidden_2_output)
y_hat = sigmoid(final_input) # shape = () 是一个标量

# 2、求误差
error = y_hat - y
# 3、反向传播
# 求输出层误差项
output_error_term = error * y_hat * (1-y_hat) # 标量
# 隐藏层误差
hidden_error = output_error_term * weights_hidden_2_output # (n_hidden,)
# 隐藏层误差项
# print(hidden_error.shape, hidden_output.shape)
hidden_error_term = hidden_error.reshape(-1) * hidden_output *(1-hidden_output)
# (n_hidden,)

del_weights_input_2_hidden += x[:, None] * hidden_error_term
del_weights_hidden_2_output += hidden_output[:, None] * output_error_term

# 更新模型权重
weights_input_2_hidden -= del_weights_input_2_hidden * learning_rate / n_records
weights_hidden_2_output -= del_weights_hidden_2_output * learning_rate / n_records

# 打印模型损失
if e % 100 == 0:
hidden_output = sigmoid(np.dot(features_train, weights_input_2_hidden))
pred_out = sigmoid(np.dot(hidden_output, weights_hidden_2_output))
loss = np.mean((pred_out - target_train)**2)

if last_loss and last_loss < loss:
print('警告:模型损失在上升, Train Loss:{}'.format(loss))
else:
print('Epochs:{} - Train Loss:{}'.format(e, loss))
last_loss = loss
#训练结束,使用测试数据集验证模型准确率
hidden = sigmoid(np.dot(feature_test,weights_input_2_hidden))
test_pred = sigmoid(np.dot(hidden,weights_hidden_2_output))
predictions = test_pred > 0.5
accuracy = np.mean(predictions == target_test)
print("Test Accuray:{:,.5f}".format(accuracy))
def get_batches(feature_train, target_train, batch_size = 32):
"""
构建批量数据的生成器
:param feature_train:
:param target_train:
:param batch_size:
:return:
"""
for ii in range(0,len(feature_train), batch_size):
batch_x = feature_train[ii:ii+batch_size]
batch_y = target_train[ii:ii+batch_size]
yield batch_x,batch_y




def gre_bp_MBGD(feature_train, target_train, feature_test, target_test,batch_size = 128):
"""
使用小批量梯度下降实现GRE
:param feature_train:
:param target_train:
:param feature_test:
:param target_test:
:return:
"""
# 1、超参数
n_hidden = 4
epochs = 2000
learning_rate = 0.06

# 获取样本数量和 特征数量
n_records, n_features = features_train.shape
last_loss = None

# 2、初始化模型权重
weights_input_2_hidden = np.random.normal(
loc=0.0, scale=0.1, size=[n_features, n_hidden]
)

weights_hidden_2_output = np.random.normal(
scale=0.1, size=[n_hidden, 1]
)

# 构建迭代次数的循环

for e in range(epochs):
# 构建存储梯度值的delta_w
# del_weights_input_2_hidden = np.zeros(weights_input_2_hidden.shape)
# del_weights_hidden_2_output = np.zeros(weights_hidden_2_output.shape)
for batch_x, batch_y in get_batches(feature_train, target_train, batch_size):
# 1、正向传播;
hidden_input = np.matmul(batch_x, weights_input_2_hidden)
hidden_output = sigmoid(hidden_input)
# hidden_output shape = (2,)

final_input = np.matmul(hidden_output, weights_hidden_2_output)
y_hat = sigmoid(final_input) # shape = () 是一个标量

# 2、求误差
error = y_hat - batch_y[:,None] #[N,1]
# 3、反向传播
# 求输出层误差项 [N,1]
output_error_term = error * y_hat * (1-y_hat) # 标量
# 隐藏层误差[N,4]
hidden_error = np.matmul(output_error_term , weights_hidden_2_output.transpose()) # (n_hidden,)
# 隐藏层误差项
# print(hidden_error.shape, hidden_output.shape)
hidden_error_term = hidden_error * hidden_output *(1-hidden_output)
# (n_hidden,)

del_weights_input_2_hidden =np.matmul(np.transpose(batch_x),hidden_error_term)/batch_size

del_weights_hidden_2_output = np.matmul(np.transpose(hidden_output),output_error_term)/batch_size

# 更新模型权重
weights_input_2_hidden -= del_weights_input_2_hidden * learning_rate / n_records
weights_hidden_2_output -= del_weights_hidden_2_output * learning_rate / n_records

# 打印模型损失
if e % 100 == 0:
hidden_output = sigmoid(np.dot(features_train, weights_input_2_hidden))
pred_out = sigmoid(np.dot(hidden_output, weights_hidden_2_output))
loss = np.mean((pred_out - target_train)**2)

if last_loss and last_loss < loss:
print('警告:模型损失在上升, Train Loss:{}'.format(loss))
else:
print('Epochs:{} - Train Loss:{}'.format(e, loss))
last_loss = loss
#训练结束,使用测试数据集验证模型准确率
hidden = sigmoid(np.dot(feature_test,weights_input_2_hidden))
test_pred = sigmoid(np.dot(hidden,weights_hidden_2_output))
predictions = test_pred > 0.5
accuracy = np.mean(predictions == target_test)
print("Test Accuray:{:,.5f}".format(accuracy))
def get_batches(feature_train, target_train, batch_size = 32):
"""
构建批量数据的生成器
:param feature_train:
:param target_train:
:param batch_size:
:return:
"""
for ii in range(0,len(feature_train), batch_size):
batch_x = feature_train[ii:ii+batch_size]
batch_y = target_train[ii:ii+batch_size]
yield batch_x,batch_y



if __name__ == '__main__':
# data_explore(admissions)
features_train, targets_train, features_test, targets_test = data_transform(admissions)
#print(features_train[:,None])
gre_bp_answer(features_train, targets_train, features_test, targets_test)


gre_bp_MBGD(features_train, targets_train, features_test, targets_test, batch_si

SGD:随机梯度下降:每次随机选择一样本,计算梯度值,并且更新模型权重。(基本不用)

BGD:计算所有样本的梯度平均值。然后技更新模型权重。

MBGD:小批量梯度下降。每次随机选择batch_size样本,计算梯度的平均值,再更新模型权重。(主要)

文章作者: Eckle
文章链接: https://wowli-up.github.io/2020/01/13/%E6%84%9F%E7%9F%A5%E5%99%A8%E6%A8%A1%E5%9E%8B/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Eckle的个人网站
打赏
  • 微信
    微信
  • 支付寶
    支付寶

评论