机器学习手撕代码(6)人工神经网络

本文介绍了如何从零开始构建一个简单的神经网络模型,包括线性层、ReLU激活函数、交叉熵损失函数和随机梯度下降优化器。通过Python代码详细展示了每个模块的实现,并给出了一个用于训练和评估的示例。此外,还提供了数据加载、可视化工具和性能指标的计算方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

机器学习手撕代码(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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值