机器学习手撕代码(6)人工神经网络
- 本篇分享一下人工神经网络的代码,ann.py为人工神经网络模型的代码,nn.py为手写实现的神经网络框架代码,utils.py中为可视化结果的工具。
- dataset见本系列第0篇。
nn.py
import numpy as np
class Module:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
pass
class Linear(Module):
def __init__(self,in_planes,out_planes):
super(Linear, self).__init__()
self.w = np.random.randn(in_planes+1,out_planes)
self.grad = None
def __call__(self,x):
x = np.concatenate([x, np.ones((x.shape[0], 1))], axis=1)
self.grad = x
res = np.dot(x,self.w)
return res
class CrossEntropyLoss(Module):
def __init__(self):
super(CrossEntropyLoss, self).__init__()
self.grad = None
def __call__(self, pred, target,eps=1e-8):
exps = np.exp(pred-np.max(pred,axis=1).reshape(pred.shape[0],1))
logits = exps / (np.sum(exps,axis=1).reshape(exps.shape[0],1)+eps)
loss = -np.sum(target*np.log(logits+eps),axis=1)
self.grad = logits-target
return loss.mean()
def backward(self):
return self.grad
class Relu(Module):
def __init__(self):
super(Relu, self).__init__()
self.grad = None
def __call__(self,x):
x[x<0] = 0.0
self.grad = np.ones_like(x)
self.grad[x<=0] = 0.0
return x
class SGD:
def __init__(self,model,lr):
self.lr = lr
self.models=[]
for att in model.__dict__:
t = model.__dict__[att]
if t.__class__.__base__ == Module:
self.models.append(t)
def backward(self,loss_fn):
bs = self.models[0].grad.shape[0]
i = np.random.randint(bs)
last_grad = loss_fn.grad[i] # 大小为该层神经元的个数,即该层的输出维度
for model in self.models[::-1]:
if 'w' in model.__dict__:
model.grad = model.grad[i].reshape((model.grad[i].shape[0],1))*last_grad
last_grad = np.sum((last_grad*model.w[:-1]),axis=1)
else:
last_grad = last_grad*model.grad[i]
def step(self):
for model in self.models[::-1]:
if 'w' in model.__dict__:
model.w = model.w-model.grad*self.lr
class MSELoss:
def __init__(self):
self.grad = None
def __call__(self, x, y):
dx = 2 * (x - y) / x.shape[1]
self.grad = dx
return np.sum(np.square(x - y)) / x.shape[0]
# class SoftMax(Module):
# def __init__(self):
# super(SoftMax, self).__init__()
# self.grad = None
# def __call__(self,x):
# exps = np.exp(x)
# res = exps / np.sum(exps)
# self.grad = res/(1-res)
# return res
ann.py
from nn import Linear,Relu,SGD,CrossEntropyLoss
import numpy as np
from utils import Metrics,DataLoader,Visualization
from sklearn.model_selection import train_test_split
from datasets.dataset import DataSet
class MyModel:
def __init__(self):
self.linear1 = Linear(12,64)
self.relu1 = Relu()
self.linear2 = Linear(64,32)
self.relu2 = Relu()
self.linear3 = Linear(32, 2)
def __call__(self,x):
x = self.linear1(x)
x = self.relu1(x)
x = self.linear2(x)
x = self.relu2(x)
x = self.linear3(x)
return x
if __name__ == '__main__':
np.random.seed(2021)
batch_size = 16
lr = 0.01
classes = 2
epoch_n = 200
dataset = DataSet('F:\PycharmProjects\machine_leatning\datasets\winequalityN.csv')
data, target, target_head, data_head = dataset.get_data()
X_train, X_test, y_train, y_test = train_test_split(data, target, random_state=2021, test_size=0.3)
train_loader = DataLoader(X_train,y_train,batch_size)
test_loader = DataLoader(X_test, y_test, batch_size)
loss_fn = CrossEntropyLoss()
model = MyModel()
opt = SGD(model, lr)
for epoch in range(epoch_n):
epoch_loss = 0
cnt = 0
train_loader.shuffle()
for batch_x,batch_y in train_loader:
batch_y_one_hot = np.eye(classes)[batch_y]
res = model(batch_x)
loss = loss_fn(res, batch_y_one_hot)
opt.backward(loss_fn)
opt.step()
epoch_loss+=loss
cnt+=1
if epoch%10 == 0:
print('epoch:%d || loss:%.6f'%(epoch,epoch_loss/cnt))
metrics_test = Metrics()
total_res = np.array([])
for batch_x, batch_y in test_loader:
batch_y_one_hot = np.eye(2)[batch_y]
res_onehot = model(batch_x)
res = np.argmax(res_onehot, axis=1)
total_res = np.concatenate([total_res,res])
metrics_test.update(res, batch_y)
metrics_test.count()
print('acc:{:.4f}'.format(metrics_test.accuracy()))
print('recall:{:.4f}'.format(metrics_test.recall()))
print('precision:{:.4f}'.format(metrics_test.precision()))
vis = Visualization(colors=['red', 'blue'])
vis.fit(X_test)
vis.savefig(y_test, total_res.astype(np.int32), './ann_diy_res_in_testset.png')
utils.py
from datasets.dataset import DataSet
import matplotlib.pyplot as plt
import numpy as np
from sklearn import manifold
from sklearn.model_selection import train_test_split
class Visualization:
def __init__(self,colors):
self.colors = colors
self.x_norm = None
def fit(self,data):
tsne = manifold.TSNE(n_components=2, init='pca', random_state=2021)
X_tsne = tsne.fit_transform(data)
x_min, x_max = X_tsne.min(0), X_tsne.max(0)
self.x_norm = (X_tsne - x_min) / (x_max - x_min)
def show(self,gt,pred):
plt.figure(figsize=(8, 8))
for i in range(self.x_norm.shape[0]):
plt.text(self.x_norm[i, 0], self.x_norm[i, 1], str(gt[i]), color=self.colors[pred[i]],
fontdict={'weight': 'bold', 'size': 8})
plt.xticks([])
plt.yticks([])
plt.show()
def savefig(self,gt,pred,file_name):
plt.figure(figsize=(8, 8))
for i in range(self.x_norm.shape[0]):
plt.text(self.x_norm[i, 0], self.x_norm[i, 1], str(gt[i]), color=self.colors[pred[i]],
fontdict={'weight': 'bold', 'size': 8})
plt.xticks([])
plt.yticks([])
plt.savefig(file_name)
class Metrics:
def __init__(self,n_classes=2):
self.n_classes = n_classes
self.confusion_matrix = [[0 for i in range(n_classes)] for j in range(n_classes)]
self.labels = []
self.preds = []
self.ALL = None
self.TN = None
self.FP = None
self.FN = None
self.TP = None
def empty(self):
self.confusion_matrix = [[0 for i in range(self.n_classes)] for j in range(self.n_classes)]
self.labels = []
self.preds = []
def update(self,preds,labels):
for i in range(len(labels)):
self.confusion_matrix[labels[i]][preds[i]]+=1
def count(self):
confusion_matrix = np.array(self.confusion_matrix)
self.ALL = np.sum(confusion_matrix)
self.TN = confusion_matrix[0,0]
self.FP = confusion_matrix[0,1]
self.FN = confusion_matrix[1,0]
self.TP = confusion_matrix[1,1]
def accuracy(self):
res = (self.TP + self.TN) / (self.TP + self.TN + self.FP + self.FN)
return res
def recall(self):
res = self.TP/(self.TP+self.FN)
return res
def precision(self):
res = self.TP/(self.TP+self.FP)
return res
class DataLoader:
def __init__(self,data,targets,batch_size):
self.batch_size = batch_size
self.data = (data - np.mean(data, axis=0)) / np.std(data,axis=0)
self.targets = targets
self.permutation = np.arange(len(data))
self.sections = self.data.shape[0] // self.batch_size
head_i = self.permutation[:self.sections * self.batch_size]
tail_i = self.permutation[self.sections * self.batch_size:]
self.index = np.array_split(head_i,self.sections)
if len(tail_i) !=0:
self.index.append(tail_i)
def shuffle(self):
self.permutation = np.random.permutation(len(self.data))
head_i = self.permutation[:self.sections * self.batch_size]
tail_i = self.permutation[self.sections * self.batch_size:]
self.index = np.array_split(head_i, self.sections)
if len(tail_i)!=0:
self.index.append(tail_i)
def __getitem__(self, i):
sub_i = self.index[i]
return self.data[sub_i],self.targets[sub_i]
if __name__ == '__main__':
dataset = DataSet('F:\PycharmProjects\machine_leatning\datasets\winequalityN.csv')
data, target, target_head, data_head = dataset.get_data()
X_train = np.concatenate([data[:200],data[-200:]])
Y_train = np.concatenate([target[:200],target[-200:]])
X_train = (X_train-np.min(X_train,axis=0))/(np.max(X_train,axis=0)-np.min(X_train,axis=0))
vis = Visualization(['red','blue'])
vis.fit(X_train)
vis.show(Y_train,Y_train)