一个与或非感知机

该博客分享了一个使用Python实现的感知机类,适用于AND、OR、NOT操作。代码中包含了权重和偏置的初始化、激活函数、训练过程、错误曲线绘制等功能,并提供了高斯正态分布数据生成的示例。通过可视化展示决策边界和训练过程,便于理解感知机的工作原理。

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

分享个python实例,自己写的一个感知机,仅供学习,自行调整参数,修改代码!

效果图:
在这里插入图片描述

代码分享:

import numpy as np
import matplotlib.pyplot as plt

class Perceptron(object):
    """
    根据课上所学,实现了一个感知机,包括AND, OR, NOT, 可以使用正态分布的样本数据
    感知机原理可参考:https://zhuanlan.zhihu.com/p/29836398  https://zhuanlan.zhihu.com/p/42438035
    https://blog.youkuaiyun.com/u012759262/article/details/101943109
    """

    def __init__(self, kind='AND'):
        self.weights = np.array([0.2, 0.4], ndmin=2)  # 初始化权重,为图方便硬编码了
        self.bias = 0.1  # 初始化偏置,为图方便硬编码了
        self.inputs = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])  # (m,2)矩阵 m:=dataSize,每个样本数据为二维,即2个特征,(f1, f2)
        if kind.upper() == 'AND':
            self.labels = np.array([[-1], [-1], [-1], [1]])  # AND  (m,1)矩阵
        elif kind.upper() == 'OR':
            self.labels = np.array([[-1], [1], [1], [1]])  # OR
        elif kind.upper() == 'NOT':
            # NOT, w2 cannot be zero, let w1 be zero, so have to make labels like this
            self.labels = np.array([[-1], [1], [-1], [1]])
        else:  # default is AND perceptron
            self.labels = np.array([[-1], [-1], [-1], [1]])  # AND  (m,1)矩阵
        self.dataSize = self.labels.size
        self.misclassifiedSet = np.array(np.zeros((self.dataSize, 1), dtype=int))  # 错分集标识初始化,(m,1)矩阵
        self.learningRate = 0.1  # adjust this when necessary
        self.errors = np.array([])  # errors in each iteration
        self.fig, self.axes = plt.subplots(1, 2)  # for plotting diagram
        self.initDiagram()
        self.firstPlot = True  # just for special usage

    def __str__(self):
        """
        输出训练后的感知机学习到的权重、偏置
        """
        return 'weights\t:%s\nbias\t:%f\n' % (self.weights, self.bias)

    def initDiagram(self):
        self.fig.set_size_inches(10, 5)  # set figure window size (10 inches * 5 inches)
        # plt.figure(figsize=(10, 5))
        self.axes[0].set_title('Perceptron Demo')
        self.axes[0].axis('scaled')
        # self.axes[0].axis('square')
        # self.axes[0].axis([-1, 2, -1, 2])
        self.axes[0].axis([0, 9, 0, 9])
        self.axes[0].set_xlabel('feature 1')
        self.axes[0].set_ylabel('feature 2')
        self.axes[0].grid()

        # self.axes[1].axis('scaled')
        self.axes[1].set_title('Cost Curve')
        # self.axes[1].axis([0, 30, -10, 0.5])
        self.axes[1].set_xlabel('iteration times')
        self.axes[1].set_ylabel('errors', color='C0')
        self.axes[1].set_xticks(range(0, 50))
        self.axes[1].grid()

    def plotBoundary(self):
        """
        图形可视化,绘制样本点分布,绘制决策边界线,要和show()配合使用
        """
        # .T就是转置, *是numpy特有的,类似C++的指针解引用,这里是二维数组解引用,即降维。而且是先运算的.T然后解引用迭代器
        # plt.plot(*self.inputs.T, 'o')  # [0 0 1 1] [0 1 0 1]
        if self.firstPlot == True:
            for i in range(self.dataSize):
                if self.labels[i] == -1:
                    self.axes[0].plot(*self.inputs[i], 'c.')  # negative sample with marker style 'c.'
                elif self.labels[i] == 1:
                    self.axes[0].plot(*self.inputs[i], 'r+')  # positive sample with maker style 'r+'
        x1 = np.linspace(-1, 6, num=3)  # not necessary to calculate too many points for it's a straight line
        if self.weights[0, 1] != 0:
            x2 = (-self.bias * np.ones(3) - self.weights[0, 0] * x1) / self.weights[0, 1]
            self.axes[0].plot(x1, x2)  # plot decision boundary line
        costX = np.arange(0, self.errors.size, 1)
        print('costX:%s' % costX)
        print('costY:%s' % self.errors)
        self.axes[1].plot(costX, self.errors, 'k-')  # plot cost curve
        plt.pause(0.1)
        self.firstPlot = False  # try comment this line *_<

    def show(self):
        plt.show()

    def adjLearningRate(self, learningRate):
        self.learningRate = learningRate

    def activation(self):
        z = self.weights.dot(self.inputs.T) + self.bias  # 激活输出Z = W·X^T+b  z.shape():=(1,m)
        return z.T  # (m,1)矩阵
        # return 1 if z >= 0 else -1

    def genMisclassifiedSet(self):
        # print('activation output:->\n%s\n<--' % self.activation())
        y = self.activation() * self.labels  # y still is (m,1)
        # print('activation multiplied by label:->\n%s\n<--' % y)
        self.misclassifiedSet.fill(0)  # remember to reset
        errSum = 0
        for i in range(self.dataSize):
            if y[i, 0] <= 0.1:  # criterion, adjust this when necessary
                errSum += y[i, 0]
                self.misclassifiedSet[i] = 1
        self.errors = np.append(self.errors, errSum)
        # print('misclassifiedSet:->\n%s\n<--' % self.misclassifiedSet)

    def training(self, iterationTimes=10):
        if iterationTimes == 10:
            iterationTimes = 50
        iterationCnt = 0
        self.genMisclassifiedSet()
        # until all samples are classified correctly, otherwise just iterate 50 times for it is linearly inseparable
        while self.misclassifiedSet.any() and iterationCnt < iterationTimes:
            iterationCnt += 1
            print('--iteration %d times--' % iterationCnt)
            for i in range(self.dataSize):
                if self.misclassifiedSet[i] == 1:
                    print('--to update param for sample %s--' % self.inputs[i])
                    # print("it's label: " + str(self.labels[i]))
                    # fixed-increment and update once by every misclassified single-sample
                    # that is, a SGD(Stochastic Gradient Descent) algorithm
                    # Cost function: C(w, b) = -Σ y·(w·x + b)  sum on every misclassified sample
                    self.weights += self.learningRate * self.labels[i] * self.inputs[i]  # 核心部分,更新权重
                    print('updated weights: ' + str(self.weights))
                    self.bias += self.learningRate * self.labels[i]  # 核心部分,更新偏置
                    print('updated bias: ' + str(self.bias))
                    # print('--updated param for sample %s--' % self.inputs[i])
            self.genMisclassifiedSet()
            # self.plotBoundary()  # 看过程
        self.plotBoundary()  # 看结果
        self.show()

    def getTraningDataset(self, inputVec, labels):
        """
        支持传入外部训练集学习参数
        :param inputVec: (m,2)矩阵,即m个样本,每个样本2个特征
        :param labels: (m,1)矩阵,即m个样本的标签值
        """
        self.inputs = inputVec
        self.labels = labels
        self.dataSize = self.labels.size
        self.misclassifiedSet = np.array(np.zeros((self.dataSize, 1), dtype=int))
        # print(self.inputs.shape)
        print(self.inputs)
        # print(self.labels.shape)
        print(self.labels)
        # print(self.dataSize)

    def normalDistribData(self, sampleAmount, kind='AND'):
        """
        调用此成员函数,将取代默认的简化感知机,使用高斯正态分布生成模拟样本数据。
        :param sampleAmount: 样本总量
        :param kind: 感知机类型,['AND', 'OR', 'NOT']三选一,不用在意大小写
        """
        if kind.upper() not in ['AND', 'OR', 'NOT']:
            return
        sampleNum = sampleAmount // 4
        mean = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
        cov = np.eye(2) * 0.01  # more bigger the coefficient more a mess
        samples = np.zeros((0, 2))  # (0,2)
        labels = np.zeros((0, 1))  # (0,1)
        for i in range(4):
            temp = np.random.multivariate_normal(mean[i], cov, sampleNum)  # 2-D normal Distribution
            samples = np.append(samples, temp, axis=0)
            if 'AND' == kind.upper():
                isPossitive = mean[i, 0] and mean[i, 1]
            elif 'OR' == kind.upper():
                isPossitive = mean[i, 0] or mean[i, 1]
            elif 'NOT' == kind.upper():
                isPossitive = mean[i, 1]  # 不考虑f1,直接取f2的值即可
            else:
                isPossitive = mean[i, 0] and mean[i, 1]
            if isPossitive:
                labels = np.append(labels, np.ones((sampleNum, 1)), axis=0)
            elif not isPossitive:
                labels = np.append(labels, np.ones((sampleNum, 1)) * -1, axis=0)
        self.getTraningDataset(samples, labels)


def gaussianData():
    positiveNum = int(200 * 0.6)
    negativeNum = int(800 * 0.6)
    mean = np.array([[2, 3], [5, 6]])
    cov = np.eye(2)
    samples = np.zeros((0, 2))
    labels = np.zeros((0, 1))
    temp = np.random.multivariate_normal(mean[0], cov, positiveNum)
    samples = np.append(samples, temp, axis=0)
    labels = np.append(labels, np.ones((positiveNum, 1)), axis=0)

    temp = np.random.multivariate_normal(mean[1], cov, negativeNum)
    samples = np.append(samples, temp, axis=0)
    labels = np.append(labels, np.ones((negativeNum, 1)) * -1, axis=0)
    # for i in range(positiveNum+negativeNum):
    #     if labels[i] == -1:
    #         plt.plot(*samples[i], 'c.')  # negative sample with marker style 'c.'
    #     elif labels[i] == 1:
    #         plt.plot(*samples[i], 'r+')  # positive sample with maker style 'r+'
    # plt.show()
    return samples, labels

if __name__ == '__main__':
    p = Perceptron('not')
    print(p)
    samples, labels = gaussianData()
    p.getTraningDataset(samples, labels)
    # p.normalDistribData(200, 'and')
    # p.normalDistribData(100, 'or')
    # p.normalDistribData(100, 'not')
    # p.genMisclassifiedSet()
    # p.plotBoundary()
    # p.show()
    # p.activation()
    p.training()
    print(p)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值