文章目录
一、线性可分
1. 准备工作
1) 数据准备:
新建txt文件,打开并写入以下数据,保存为images2.csv文件(把横向分配为1、纵向分配为0)
x1,x2,y 153,432,0 220,262,0 118,214,0 474,384,1 485,411,1 233,430,0 396,321,1 484,349,1 429,259,1 286,220,1 399,433,0 403,300,1 252,34,1 497,372,1 379,416,0 76,163,0 263,112,1 26,193,0 61,473,0 420,253,1
2)标准化
对特征进行标准化(Z-Score),使均值为0,标准差为1,加速模型收敛
import numpy as np
import matplotlib.pyplot as plt
# 读入训练数据
train = np.loadtxt('images2.csv', delimiter=',', skiprows=1)
train_x = train[:,0:2]
train_y = train[:,2]
# 参数初始化
theta = np.random.rand(3)
# 标准化
mu = train_x.mean(axis=0)
sigma = train_x.std(axis=0)
def standardize(x):
return (x - mu) / sigma
train_z = standardize(train_x)
# 增加 x0
def to_matrix(x):
x0 = np.ones([x.shape[0], 1])
return np.hstack([x0, x])
X = to_matrix(train_z)
plt.plot(train_z[train_y == 1, 0], train_z[train_y == 1, 1], 'o')
plt.plot(train_z[train_y == 0, 0], train_z[train_y == 0, 1], 'x')
plt.show()
标准化后的图:
3)参数更新表达式和判别式
与回归时一样,将当作向量来处理,将它与训练数据的矩阵相乘,把重复次数设置得稍微多一点,比如5000次左右。在实际问题中需要通过反复尝试来设置这个值,即通过确认学习中的精度来确定重复多少次才足够好。
在逻辑回归中,这条直线是决策边界,也就是说,
x⩾0时图像是横向的,
x<0时图像是纵向的。,将
x=0变形并加以整理,得到这样的表达式:
2. 完整代码
import numpy as np
import matplotlib.pyplot as plt
# 读入训练数据
train = np.loadtxt('images2.csv', delimiter=',', skiprows=1)
train_x = train[:,0:2]
train_y = train[:,2]
# 参数初始化
theta = np.random.rand(3)
# 对特征进行标准化(Z-Score),使均值为0,标准差为1,加速模型收敛
mu = train_x.mean(axis=0)
sigma = train_x.std(axis=0)
def standardize(x):
return (x - mu) / sigma
train_z = standardize(train_x)
# 增加 x0
def to_matrix(x):
x0 = np.ones([x.shape[0], 1])
return np.hstack([x0, x]) # 组合成矩阵 [x0, x1, x2]
X = to_matrix(train_z)
# Sigmoid函数将线性组合 θ^T·x 映射到(0,1)区间,表示概率
def f(x):
return 1 / (1 + np.exp(-np.dot(x, theta)))
# 分类函数
def classify(x):
return (f(x) >= 0.5).astype(np.int) # 根据概率是否≥0.5将样本分类为0或1
# 学习率
ETA = 1e-3
# 重复次数
epoch = 5000
# 更新次数
count = 0
# 重复学习 梯度下降
for _ in range(epoch):
theta = theta - ETA * np.dot(f(X) - train_y, X)
# 日志输出
count += 1
print('第 {} 次 : theta = {}'.format(count, theta))
# 绘图确认
x0 = np.linspace(-2, 2, 100)
plt.plot(train_z[train_y == 1, 0], train_z[train_y == 1, 1], 'o')
plt.plot(train_z[train_y == 0, 0], train_z[train_y == 0, 1], 'x')
plt.plot(x0, -(theta[0] + theta[1] * x0) / theta[2], linestyle='dashed')
plt.show()
3. 结果展示
4. 验证
输入: print(classify(to_matrix(standardize([
[200,100], # 200×100 的横向图像
[100,200] # 100×200 的纵向图像)输出:[1, 0] # 200×100被分类为横向,而100×200被分 类为纵向了
二、线性不可分
1. 准备工作
1)数据准备:
新建txt文件,打开并写入以下数据,保存为data3.csv文件
x1,x2,y 0.54508775,2.34541183,0 0.32769134,13.43066561,0 4.42748117,14.74150395,0 2.98189041,-1.81818172,1 4.02286274,8.90695686,1 2.26722613,-6.61287392,1 -2.66447221,5.05453871,1 -1.03482441,-1.95643469,1 4.06331548,1.70892541,1 2.89053966,6.07174283,0 2.26929206,10.59789814,0 4.68096051,13.01153161,1 1.27884366,-9.83826738,1 -0.1485496,12.99605136,0 -0.65113893,10.59417745,0 3.69145079,3.25209182,1 -0.63429623,11.6135625,0 0.17589959,5.84139826,0 0.98204409,-9.41271559,1 -0.11094911,6.27900499,0
2)标准化:
import numpy as np
import matplotlib.pyplot as plt
# 读入训练数据
train = np.loadtxt('data3.csv', delimiter=',', skiprows=1)
train_x = train[:,0:2]
train_y = train[:,2]
# 参数初始化
theta = np.random.rand(4)
# 标准化
mu = train_x.mean(axis=0)
sigma = train_x.std(axis=0)
def standardize(x):
return (x - mu) / sigma
train_z = standardize(train_x)
plt.plot(train_z[train_y == 1, 0], train_z[train_y == 1, 1], 'o')
plt.plot(train_z[train_y == 0, 0], train_z[train_y == 0, 1], 'x')
plt.show()
数据呈现:
考虑用二次函数分类,在训练数据里加上,增加一个θ3参数,参数总数达到四个。
sigmoid函数和参数更新部分与刚才完全一样。
对于有四个参数的=0可以这样变形,然后按这个公式画图
2. 完整代码
import numpy as np
import matplotlib.pyplot as plt
# 读入训练数据
train = np.loadtxt('data3.csv', delimiter=',', skiprows=1)
train_x = train[:,0:2]
train_y = train[:,2]
# 参数初始化
theta = np.random.rand(4) # theta有四个参数对应四个特征:θ₀:截距项系数,θ₁:x₁的系数,θ₂:x₂的系数,θ₃:x₁²的系数
# 标准化
mu = train_x.mean(axis=0)
sigma = train_x.std(axis=0)
def standardize(x):
return (x - mu) / sigma
train_z = standardize(train_x)
# 增加 x0 和 x3
def to_matrix(x):
x0 = np.ones([x.shape[0], 1])
x3 = x[:,0,np.newaxis] ** 2
return np.hstack([x0, x, x3])
X = to_matrix(train_z)
# sigmoid 函数
def f(x):
return 1 / (1 + np.exp(-np.dot(x, theta)))
# 分类函数
def classify(x):
return (f(x) >= 0.5).astype(np.int)
# 学习率
ETA = 1e-3
# 重复次数
epoch = 5000
# 更新次数
count = 0
# 重复学习
for _ in range(epoch):
theta = theta - ETA * np.dot(f(X) - train_y, X)
# 日志输出
count += 1
print('第 {} 次 : theta = {}'.format(count, theta))
# 绘图确认
x1 = np.linspace(-2, 2, 100)
x2 = -(theta[0] + theta[1] * x1 + theta[3] * x1 ** 2) / theta[2]
plt.plot(train_z[train_y == 1, 0], train_z[train_y == 1, 1], 'o')
plt.plot(train_z[train_y == 0, 0], train_z[train_y == 0, 1], 'x')
plt.plot(x1, x2, linestyle='dashed')
plt.show()
3. 结果展示
4. 查看精度
将重复次数作为横轴、精度作为纵轴来绘图。
np.mean()将布尔值转换为1(True)和0(False)后求平均相当于计算:正确样本数 / 总样本数 × 100% 。
假设真实标签 [1,0,1,1], 预测结果 [1,1,1,0],比较结果 [True, False, True, False],平均值 (1+0+1+0)/4 = 0.5 → 50%准确率。
代码修改部分:
# 记录精度变化
accuracies = []
# 重复学习
for i in range(epoch):
# 参数更新
theta = theta - ETA * np.dot(f(X) - train_y, X)
# 计算当前精度
current_pred = classify(X)
accuracy = np.mean(current_pred == train_y)
accuracies.append(accuracy)
plt.subplot(1, 2, 2)
plt.plot(range(1, epoch+1), accuracies)
plt.grid(True)
plt.tight_layout()
plt.show()
从图中可以看出,在重复满5000次之前(1000多时),精度已经到1.0了。可以像这样,每次学习后都计算精度,当精度达到满意的程度后就停止学习。
三、随机梯度下降法
就是把学习部分稍稍修改一下,修改:
for _ in range(epoch):
# 使用随机梯度下降法更新参数
p = np.random.permutation(X.shape[0])
for x, y in zip(X[p,:], train_y[p]):
theta = theta - ETA * (f(x) - y) * x
效果如图:分类的也很好!