多层感知器算是神经网络里面最简单的一种了。
将x丢到线性函数里面,给一个权重w和b然后把计算值再丢给激活函数这样的东西就是一个神经元。之后输出的值传递到下一层神经网络的神经元这样的过程就是前馈。接下来用误差函数计算层层传递之后用输出值和训练数据间的差值之后回过头来想办法调整之前输入的权重和噪音b使得这个误差变得尽可能小,这里面的一种办法就是梯度下降法来纠正误差。这个过程就是反向传播。
简单的神经网络大概就是这么回事。如果传递的层数设定的很多的话就可以称其为深层神经网络了。
这次用numpy和一个简单的数据集实装一个有基本功能的神经网络。之后用pytorch和Ames数据集来实装一个加了一些优化功能的DNN。
第一部分: 一个简单神经网络
首先是数据集
import numpy as np
x = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
t = np.array([[0], [1], [1], [0]])
这是一个简单的分类问题,就01两种答案。因此这样的问题使用交叉熵函数作为误差函数先准备一下。
def compute_loss(t, y):
return (-t * np.log(y) - (1 - t) * np.log(1 - y)).sum
然后二分类问题所用的激活函数就是sigmoid函数。当然激活函数还有很多种这里就不细讲了。有了激活函数还要一个函数来判断激活函数输出的信号到底是0还是1就把这个函数叫做dsigmoid。
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def dsigmoid(x):
return sigmoid(x) * (1 - sigmoid(x))
然后接下来开始配置神经网络。这里因为相对复杂所以准备一个class。准备了几个初始变量也就是输入层,隐藏层和输出层的次元。这是一个三层的网络。这里l1对应输入层和隐藏层l2对应隐藏层和输出层。激活函数什么的都安排一下。然后呼出前馈,也就是把计算的结果丢给下一层的神经元return y。
class MLP(object):
def __init__(self, input_dim, hidden_dim, output_dim):
self.l1 = Layer(input_dim=input_dim,
output_dim=hidden_dim,
activation=sigmoid,
dactivation=dsigmoid)
self.l2 = Layer(input_dim=hidden_dim,
output_dim=output_dim,
activation=sigmoid,
dactivation=dsigmoid)
self.layers = [self.l1, self.l2]
def __call__(self, x):
return self.forward(x)
def forward(self, x):
h = self.l1(x)
y = self.l2(h)
return y
接下来是层与层之间连接处的class。一开始权重W初始值这里设定为随机值,b为全部都是0,之后调用前馈, 在激活之前的量也就是_pre_activation 他是np.matmul(x, self.W) + self.b,这个就是Wx+b。之后返回计算值传递给下一层。然后反向传播就是在末尾拿到传递过来的信号丢到激活函数里面判断是0是1,之后返回这个计算值delta之后计算梯度。这里dW就是X^T和delta的点积,db就是1^T和delta的乘积,这部分和逻辑回归是一样的。
class Layer(object):
def __init__(self, input_dim, output_dim,
activation, dactivation):
self.W = np.random.normal(size=(input_dim, output_dim))
self.b = np.zeros(output_dim)
self.activation = activation
self.dactivation = dactivation
def __call__(self, x):
return self.forward(x)
def forward(self, x):
self._input = x
self._pre_activation = np.matmul(x, self.W) + self.b
return self.activation(self._pre_activation)
def backward(self, delta, W):
delta = self.dactivation(self._pre_activation) \
* np.matmul(delta, W.T)
return delta
def compute_gradients(self, delta):
dW = np.matmul(self._input.T, delta)
db = np.matmul(np.ones(self._input.shape[0]), delta)
return dW, db
接下来是训练,这里用一个函数整理起来。这里的学习率设定为0.1。y就是模型的输出。用一个for文来循环神经网络的层。最后输出误差。这里的模型输入的维度是2,中间层维度发散到3,最后输出的是0或1所以输出维度是1。
model = MLP(2, 3, 1)
def train_step(x, t):
y = model(x)
for i, layer in enumerate(model.layers[::-1]):
if i == 0:
delta = y - t
else:
delta = layer.backward(delta, W)
dW, db = layer.compute_gradients(delta)
layer.W = layer.W - 0.1 * dW
layer.b = layer.b - 0.1 * db
W = layer.W
loss = compute_loss(t, y)
return loss
深度学习的训练首先是把大量数据划分成很多个子集,每一个子集就是batch,batch里面样本数量就是batch size。将这几个子集依次训练,训练的次数就用变量epoch来表示。以下就是训练步骤。最后用训练好的模型测试一下得到结果。
epochs = 100
for epoch in range(epochs):
train_loss = train_step(x, t)
if epoch % 100 == 0 or epoch == epochs - 1:
print(epoch: {}, loss: {:.3f}.format(
epoch+1,train_loss))
for input in x:
print({} => {:.3f}.format(input, model(input)[0]))
第二部分: 使用pytorch实装一个DNN使用Ames数据集
首先是导入数据并进行简单清洗,这里使用maccskey作为descriptor。一共是6506个数据,维度是167,所以输入的维度也是167。注意一下从pandas拿出来的y要换一下格式y=df[activity].values.reshape(-1, 1)这样。
import numpy as np
import pandas as pd
import urllib.request
from rdkit import Chem
from rdkit.Chem import AllChem, Draw, Descriptors, PandasTools
from rdkit.ML.Descriptors import MoleculeDescriptors
from sklearn.model_selection import train_test_split
url = https://raw.githubusercontent.com/onecoinbuybus/Database_chemoinformatics/master/smiles_cas_N6512.smi
urllib.request.urlretrieve(url, ames.txt)
df = pd.read_csv(ames.txt,header=None, sep=\t)
df.columns = [smiles, CAS_NO, activity]
PandasTools.AddMoleculeColumnToFrame(frame=df, smilesCol=smiles)
none_list=[]
for i in range(df.shape[0]):
if Chem.MolFromSmiles(df[smiles][i]) is None:
none_list.append(i)
df=df.drop(none_list)
mols=[Chem.MolFromSmiles(smile) for smile in df[smiles]]
maccskeys = []
for m in mols:
maccskey = [x for x in AllChem.GetMACCSKeysFingerprint(m)]
maccskeys.append(maccskey)
X = np.array(maccskeys)
y=df[activity].values.reshape(-1, 1)
X.shape
#(6506, 167)
x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
接下来是神经网络模型
这里有l1l2l3l4相比上一个l1l2多了两层。class的初始变量就是输入的维度,中间层的维度和输出的维度。然后中间的一些激活函数换成了relu,也是一个适用于分类问题的激活函数。
设定了dropout,这里概率设了0.3。dropout会给神经网络里面的神经元套上0或者1的小面具随机的除去一些神经元来防止过学习。
对于训练的batch,这里导入了batch normalization,也就是nn.BatchNorm1d将数据预处理使得其平均值为0,方差1
关于初始值的设定,这里要对Wx +b里面的W和b的初始值进行一个设定,这里也是给了一个符合正态分布的初始值,这种方法就叫做 He initialization,这里的He是何,以作者命名。
最后是forward前馈给下一层。
from sklearn.utils import shuffle
from sklearn.metrics import accuracy_score
import torch
import torch.nn as nn
import torch.optim as optimizers
class DNN(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim):
super().__init__()
self.l1 = nn.Linear(input_dim, hidden_dim)
self.b1 = nn.BatchNorm1d(hidden_dim)
self.a1 = nn.ReLU()
self.d1 = nn.Dropout(0.3)
self.l2 = nn.Linear(hidden_dim, hidden_dim)
self.b2 = nn.BatchNorm1d(hidden_dim)
self.a2 = nn.ReLU()
self.d2 = nn.Dropout(0.3)
self.l3 = nn.Linear(hidden_dim, hidden_dim)
self.b3 = nn.BatchNorm1d(hidden_dim)
self.a3 = nn.Sigmoid()
self.d3 = nn.Dropout(0.3)
self.l4 = nn.Linear(hidden_dim, output_dim)
self.layers = [self.l1, self.b1, self.a1, self.d1,
self.l2, self.b2, self.a2, self.d2,
self.l3, self.b3, self.a3, self.d3,
self.l4]
for layer in self.layers:
if type(layer) == nn.Linear:
nn.init.kaiming_normal_(layer.weight)
def forward(self, x):
for layer in self.layers:
x = layer(x)
return x
接下来设定神经网络,输入维度167,中间100,输出1。
判断标准是使用了BCE也就是binary cross entrop交叉熵函数。
optimizer优化方法这里使用了Adam当然也可以用SGD随机梯度降下等等很多很多的优化方法。然后学习率设定为0.01。
compute loss 使用交叉熵函数输出误差。
训练用的函数train_step使用相关的优化方法再调用交叉熵函数计算出误差。
epoch设定为100,batchsize设定为epoch数的1/10就是10
model = DNN(167, 100, 1).to(device)
criterion = nn.BCEWithLogitsLoss()
#optimizer = optimizers.SGD(model.parameters(), lr=0.01)
optimizer = optimizers.Adam(model.parameters(),lr=0.01,
betas=(0.9, 0.999), amsgrad=True)
def compute_loss(t, y):
return criterion(y, t)
def train_step(x, t):
model.train()
preds = model(x)
loss = compute_loss(t, preds)
optimizer.zero_grad()
loss.backward()
optimizer.step()
return loss
epochs = 100
batch_size = 10
n_batches = x_train.shape[0] // batch_size
接下来就是正式开始训练,将数据集洗牌之后取出来转化为tensor,之后划分batch,然后用for循环重复训练,可以从print里面看到训练loss的变化。
for epoch in range(epochs):
train_loss = 0.
x_, t_ = shuffle(x_train, t_train)
x_ = torch.Tensor(x_).to(device)
t_ = torch.Tensor(t_).to(device)
for n_batch in range(n_batches):
start = n_batch * batch_size
end = start + batch_size
loss = train_step(x_[start:end], t_[start:end])
train_loss += loss.item()
print(epoch: {}, loss: {:.3}.format(
epoch+1,
train_loss
))
跑完100个epoch训练完成。之后看看训练好的模型, 准备一个评价用的函数。
def val_step(x):
model.eval()
preds = model(x)
return preds
最后将测试集转化为tensor丢到这个函数里面得到预测的结果
#tensor化
x_test = torch.from_numpy(x_test.astype(np.float32))
x_test = x_test.to(device)
preds=[]
for i in range(len(x_test)):
pred=val_step(x_test)
preds.append(pred[i].argmax(dim=-1).tolist())
再对y稍微处理一下用sklearn的标准来评价结果
from sklearn.metrics import accuracy_score
test=np.array(y_test.tolist()).ravel()
accuracy_score(test,preds)
....我这个神经网络不太精神可能还需要哪里微调什么的..
》的全部内容,本文网址:https://www.7ca.cn/baike/70260.shtml,如对您有帮助可以分享给好友,谢谢。