参考资料:
题目要求
- 手动实现感知机
- 对模型评估
基本原理
直观定义
:感知机接受多个输入信号,输出一个信号,输入信号配以权重,用阈值 $\theta$ 判定这个神经元是否被激活。
正规定义
:感知机是根据输入实例的特征向量 $x$ ,对其进行 二分类 的线性分类模型:
$$f(x)=sign(w \cdot x+b)$$
感知机学习算法,是基于随机梯度下降法的对损失函数的最优化算法,有 原始形式
和 对偶形式
。
随机梯度下降法 Stochastic Gradient Descent,随机抽取一个误分类点使其梯度下降。
二范数:$\Vert x \Vert_2$,即距离
损失函数:
$$L(w,b)=−\sum_{x_i \in M}y_i(w⋅x_i+b)$$
代码实现
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
# iris数据集5个属性:花萼长、花萼宽、花瓣长、花瓣宽、种类(3种)
# 每种花各有 50 个数据
def get_data():
iris = load_iris()
#print(iris)
df = pd.DataFrame(iris.data, columns = iris.feature_names)
df['label'] = iris.target
df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
data = np.array(df.iloc[:100, [0, 1, -1]])
X, y = data[:,:-1], data[:,-1]
y = np.array([1 if i == 1 else -1 for i in y])
return X, y
def show_model(model):
x_ = np.linspace(4, 7, 100)
y_ = -(model.w[0] * x_ + model.b)/model.w[1]
plt.scatter([i[0] for i in X[:50]], [i[1] for i in X[:50]], label='0') # scatter散点
plt.scatter([i[0] for i in X[50:]], [i[1] for i in X[50:]], label='1')
plt.plot(x_, y_)
plt.title("After")
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
plt.show()
class Perceptron:
def __init__(self):
self.w = np.zeros(2)
self.b = 0
self.l_rate = 0.1 #步长
def sign(self, x, w, b):
return np.dot(w, x) + b
def fit_yuan_shi(self, X_train, y_train):
flag = False # 是否完成
while not flag:
wrong_count = 0
for d in range(len(X_train)):
X = X_train[d]
y = y_train[d]
if y * self.sign(X, self.w, self.b) <= 0:
self.w = self.w + self.l_rate * np.dot(y, X)
self.b = self.b + self.l_rate * y
wrong_count += 1
if wrong_count == 0:
flag = True
def fit_dui_ou(self, X_train, y_train):
n = len(X_train)
flag = False # 是否完成
gram = np.array([np.dot(i,j) for i in X_train for j in X_train]).reshape(n, n)
alpha = [0]*n
while not flag:
wrong_count = 0
for d in range(n):
X = X_train[d]
y = y_train[d]
#print("No.{}:".format(d),X,y,alpha)
if y * (sum([alpha[i]*y_train[i]*gram[d][i] for i in range(n)]) + self.b) <= 0:
alpha[d] += 1 #学习率为 1
self.b += y
wrong_count += 1
if wrong_count == 0:
flag = True
self.w[0] = sum([alpha[i]*y_train[i]*X_train[i][0] for i in range(n)])
self.w[1] = sum([alpha[i]*y_train[i]*X_train[i][1] for i in range(n)])
#if __name__ == "__main__":
X, y = get_data()
plt.scatter([i[0] for i in X[:50]], [i[1] for i in X[:50]], label='0')
plt.scatter([i[0] for i in X[50:]], [i[1] for i in X[50:]], label='1')
plt.title("Before")
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
plt.show()
model1, model2 = Perceptron(), Perceptron()
model1.fit_yuan_shi(X, y)
show_model(model1)
model2.fit_dui_ou([[3,3],[4,3],[1,1]], [1,1,-1])
X, y = [[3,3],[4,3],[1,1]], [1,1,-1]
x_ = np.linspace(1, 4, 100)
y_ = -(model2.w[0] * x_ + model2.b)/model2.w[1]
plt.scatter([i[0] for i in X[:2]], [i[1] for i in X[:2]], label='0') # scatter散点
plt.scatter([i[0] for i in X[2:]], [i[1] for i in X[2:]], label='1')
plt.plot(x_, y_)
plt.title("After")
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
plt.show()
#对偶形式在这效率好低,如果用水仙花数据就跑不出来,alpha半天只改动了前几项
对偶形式应该没问题吧…