-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
68d1a9d
commit 0de2487
Showing
4 changed files
with
130 additions
and
34 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,10 @@ | ||
# 感知机(Perceptron)教程 | ||
# 李航 《统计学习方法》 第二版 代码复现以及相关公式推导 | ||
#代码注释会写得很详细. | ||
|
||
## 感知机简介 | ||
感知机是最简单的人工神经网络形式之一,用于解决二元分类问题。 | ||
参考repo lihang-code | ||
|
||
## 感知机的工作原理 | ||
|
||
### 大致思想 | ||
- 感知机接收多个输入(x1, x2, ..., xn),每个输入都有一个对应的权重(w1, w2, ..., wn)。 | ||
- 输入和权重相乘并求和,加上偏置项(bias)。 | ||
- 将得到的结果传递给激活函数(通常为阶跃函数),输出最终的分类结果。 | ||
|
||
### 数学原理 | ||
感知机的数学表达式如下所示: | ||
- 输入和权重的线性组合: \( \sum_{i=1}^{n} w_i \cdot x_i + b \) | ||
- 阶跃函数: \( f(x) = \begin{cases} 1, & \text{if } \sum_{i=1}^{n} w_i \cdot x_i + b > 0 \\ 0, & \text{otherwise} \end{cases} \) | ||
Bug修正 | ||
|
||
### 权重更新算法 | ||
感知机的学习规则使用简单的权重更新算法,通过随机梯度下降(Stochastic Gradient Descent)来更新权重和偏置项以最小化误差。 | ||
在每一轮训练中,对于每个输入样本(xi)和对应的真实标签(yi),权重更新规则如下: | ||
- \( w_i = w_i + \alpha \cdot (y_i - \hat{y_i}) \cdot x_i \),其中 \(\alpha\) 是学习率(learning rate),\(\hat{y_i}\) 是预测值,\(y_i\) 是真实值。 | ||
- \( b = b + \alpha \cdot (y_i - \hat{y_i}) \) | ||
|
||
### 对偶形式 | ||
感知机还有一种对偶形式,使用输入数据和标签的内积(dot product)计算权重更新。 | ||
对于权重更新,可以使用以下公式进行更新: | ||
- \( w_i = w_i + \alpha \cdot (y_i - \hat{y_i}) \cdot x_i \) | ||
- \( b = b + \alpha \cdot (y_i - \hat{y_i}) \cdot 1 \),其中 \(x_i\) 和 \(1\) 是输入数据和偏置项,通过内积计算。 | ||
|
||
|
||
|
||
## 总结 | ||
感知机是最简单的神经网络形式之一,通过调整权重和偏置项,它可以学习并完成简单的二元分类任务。 | ||
|
||
## 文件解释 | ||
eg1.py 原始问题 | ||
eg_dual_form.py 对偶形式(起始就是计算下Gram矩阵) | ||
# 2023/11/30 提升方法 eg8.1 lihang-code运行出来最后score=0.4 而书上是1.0 原因是lihang-code里把G3(x)弄错了(应该为positive),关键在于 if weight_error_positive <= weight_error_nagetive: #这里应该改成<= 代码第60行。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
|
||
|
||
|
||
|
||
|
||
import numpy as np | ||
|
||
|
||
class AdaBoost: | ||
def __init__(self, X, y, tol=0.05, max_iter=10): | ||
# 训练数据 实例 | ||
self.X = X | ||
# 训练数据 标签 | ||
self.y = y | ||
# 训练中止条件 right_rate>self.tol | ||
self.tol = tol | ||
# 最大迭代次数 | ||
self.max_iter = max_iter | ||
# 初始化样本权重w | ||
self.w = np.full((X.shape[0]), 1 / X.shape[0]) | ||
self.G = [] # 弱分类器 | ||
|
||
def build_stump(self): #决策树桩 | ||
""" | ||
以带权重的分类误差最小为目标,选择最佳分类阈值 | ||
best_stump['dim'] 合适的特征所在维度 | ||
best_stump['thresh'] 合适特征的阈值 | ||
best_stump['ineq'] 树桩分类的标识lt,rt | ||
""" | ||
m, n = np.shape(self.X) | ||
# 分类误差 | ||
e_min = np.inf | ||
# 小于分类阈值的样本属于的标签类别 | ||
sign = None | ||
# 最优分类树桩 | ||
best_stump = {} | ||
for i in range(n): | ||
range_min = self.X[:, i].min() # 求每一种特征的最大最小值 | ||
range_max = self.X[:, i].max() | ||
step_size = (range_max - range_min) / n | ||
for j in range(-1, int(n) + 1): | ||
thresh_val = range_min + j * step_size | ||
# 计算左子树和右子树的误差 | ||
for inequal in ['lt', 'rt']: | ||
predict_vals = self.base_estimator(self.X, i, thresh_val, | ||
inequal) | ||
err_arr = np.array(np.ones(m)) | ||
err_arr[predict_vals.T == self.y.T] = 0 | ||
weighted_error = np.dot(self.w, err_arr) | ||
if weighted_error < e_min: | ||
e_min = weighted_error | ||
sign = predict_vals | ||
best_stump['dim'] = i | ||
best_stump['thresh'] = thresh_val | ||
best_stump['ineq'] = inequal | ||
return best_stump, sign, e_min | ||
|
||
def updata_w(self, alpha, predict): | ||
""" | ||
更新样本权重w | ||
""" | ||
# 以下2行根据公式8.4 8.5 更新样本权重 | ||
P = self.w * np.exp(-alpha * self.y * predict) | ||
self.w = P / P.sum() | ||
|
||
@staticmethod | ||
def base_estimator(X, dimen, threshVal, threshIneq): | ||
""" | ||
计算单个弱分类器(决策树桩)预测输出 | ||
""" | ||
ret_array = np.ones(np.shape(X)[0]) # 预测矩阵 | ||
# 左叶子 ,整个矩阵的样本进行比较赋值 | ||
if threshIneq == 'lt': | ||
ret_array[X[:, dimen] <= threshVal] = -1.0 | ||
else: | ||
ret_array[X[:, dimen] > threshVal] = -1.0 | ||
return ret_array | ||
|
||
def fit(self): | ||
""" | ||
对训练数据进行学习 | ||
""" | ||
G = 0 | ||
for i in range(self.max_iter): | ||
best_stump, sign, error = self.build_stump() # 获取当前迭代最佳分类阈值 | ||
alpha = 1 / 2 * np.log((1 - error) / error) # 计算本轮弱分类器的系数 | ||
# 弱分类器权重 | ||
best_stump['alpha'] = alpha | ||
# 保存弱分类器 | ||
self.G.append(best_stump) | ||
# 以下3行计算当前总分类器(之前所有弱分类器加权和)分类效率 | ||
G += alpha * sign | ||
y_predict = np.sign(G) | ||
error_rate = np.sum( | ||
np.abs(y_predict - self.y)) / 2 / self.y.shape[0] | ||
if error_rate < self.tol: # 满足中止条件 则跳出循环 | ||
print("迭代次数:", i + 1) | ||
break | ||
else: | ||
self.updata_w(alpha, y_predict) # 若不满足,更新权重,继续迭代 | ||
|
||
def predict(self, X): | ||
""" | ||
对新数据进行预测 | ||
""" | ||
m = np.shape(X)[0] | ||
G = np.zeros(m) | ||
for i in range(len(self.G)): | ||
stump = self.G[i] | ||
# 遍历每一个弱分类器,进行加权 | ||
_G = self.base_estimator(X, stump['dim'], stump['thresh'], | ||
stump['ineq']) | ||
alpha = stump['alpha'] | ||
G += alpha * _G | ||
y_predict = np.sign(G) | ||
return y_predict.astype(int) | ||
|
||
def score(self, X, y): | ||
"""对训练效果进行评价""" | ||
y_predict = self.predict(X) | ||
error_rate = np.sum(np.abs(y_predict - y)) / 2 / y.shape[0] | ||
return 1 - error_rate | ||
|