Python机器学习经典实例 - 学习笔记
第1章 监督学习
核心思想:在有标记样本上建立模型,学习输入与输出之间的映射关系,用于预测连续值输出。
1.1 数据预处理技术
是什么? 将原始数据转换为机器学习算法可理解的格式的技术集合。
为什么需要? 真实世界的原始数据通常包含噪声、不同量纲、缺失值,算法无法直接处理。
作用?
- 消除特征间的偏差
- 统一数据量纲
- 提高模型训练效率和准确性
主要技术:
| 技术 | 作用 | 适用场景 |
|---|---|---|
| 均值移除(标准化) | 特征均值为0,标准差为1 | 消除量纲影响 |
| 范围缩放 | 将特征缩放到指定范围(如0-1) | 需要固定数值范围 |
| 归一化(L1) | 特征向量数值之和为1 | 确保数据可比性 |
| 二值化 | 数值转布尔类型 | 需要离散特征 |
| 独热编码 | 将分类变量转为二进制向量 | 处理类别特征 |
对应代码:
python
# 数据预处理示例
import numpy as np
from sklearn import preprocessing
data = np.array([[3, -1.5, 2, -5.4], [0, 4, -0.3, 2.1], [1, 3.3, -1.9, -4.3]])
# 1. 均值移除(标准化)
data_standardized = preprocessing.scale(data)
# 2. 范围缩放
data_scaler = preprocessing.MinMaxScaler(feature_range=(0, 1))
data_scaled = data_scaler.fit_transform(data)
# 3. 归一化
data_normalized = preprocessing.normalize(data, norm='l1')
# 4. 二值化
data_binarized = preprocessing.Binarizer(threshold=1.4).transform(data)
# 5. 独热编码
encoder = preprocessing.OneHotEncoder()
encoder.fit([[0, 2, 1, 12], [1, 3, 5, 3], [2, 3, 2, 12], [1, 2, 4, 3]])
encoded_vector = encoder.transform([[2, 3, 5, 3]]).toarray()
1.2 标记编码方法
是什么? 将单词/字符串标记转换为数值形式的技术。
为什么需要? 机器学习算法只能处理数值数据,而实际应用中标记常以人类可理解的单词形式存在。
作用? 建立单词与数字的双向映射,便于算法处理和结果解释。
局限? 编码后的数字可能被误解为有大小关系(如0<1<2),但实际上类别间可能是无序的。
[由此引出:独热编码解决类别顺序问题]
对应代码:
python
# 标记编码示例
from sklearn import preprocessing
label_encoder = preprocessing.LabelEncoder()
input_classes = ['audi', 'ford', 'audi', 'toyota', 'ford', 'bmw']
# 训练编码器
label_encoder.fit(input_classes)
# 编码转换
labels = ['toyota', 'ford', 'audi']
encoded_labels = label_encoder.transform(labels) # [3, 2, 0]
# 反向解码
encoded_labels = [2, 1, 0, 3, 1]
decoded_labels = label_encoder.inverse_transform(encoded_labels)
1.3 线性回归
是什么? 用输入变量的线性组合来估计输出变量的监督学习算法。
为什么需要? 预测连续值输出(如房价、温度等)。
作用? 建立输入与输出的线性映射关系,进行预测。
目标函数(OLS):
最小化残差平方和:min∑(yi−y^i)2\min \sum(y_i - \hat{y}_i)^2min∑(yi−y^i)2
局限?
- 只能拟合线性关系
- 对异常值敏感
[由此引出:岭回归解决异常值敏感问题]
对应代码:
python
# 线性回归示例
import numpy as np
from sklearn import linear_model
import matplotlib.pyplot as plt
# 加载数据
X = []
y = []
with open('data_singlevar.txt', 'r') as f:
for line in f.readlines():
xt, yt = [float(i) for i in line.split(',')]
X.append(xt)
y.append(yt)
# 划分训练集和测试集
num_training = int(0.8 * len(X))
X_train = np.array(X[:num_training]).reshape((num_training, 1))
y_train = np.array(y[:num_training])
X_test = np.array(X[num_training:]).reshape((len(X) - num_training, 1))
y_test = np.array(y[num_training:])
# 创建并训练线性回归器
linear_regressor = linear_model.LinearRegression()
linear_regressor.fit(X_train, y_train)
# 预测
y_train_pred = linear_regressor.predict(X_train)
y_test_pred = linear_regressor.predict(X_test)
# 可视化
plt.scatter(X_train, y_train, color='green')
plt.plot(X_train, y_train_pred, color='black', linewidth=4)
plt.title('Training data')
plt.show()
1.4 回归评估指标
是什么? 衡量回归器拟合效果的量化指标。
常用指标:
| 指标 | 公式/定义 | 特点 |
|---|---|---|
| 平均绝对误差(MAE) | 1n∑∣yi−y^i∣\frac{1}{n}\sum|y_i - \hat{y}_i|n1∑∣yi−y^i∣ | 直观,单位与目标一致 |
| 均方误差(MSE) | 1n∑(yi−y^i)2\frac{1}{n}\sum(y_i - \hat{y}_i)^2n1∑(yi−y^i)2 | 惩罚大误差,最常用 |
| 中位数绝对误差 | median(∣yi−y^i∣)median(|y_i - \hat{y}_i|)median(∣yi−y^i∣) | 抗异常值干扰 |
| 解释方差分 | 1−Var(y−y^)Var(y)1 - \frac{Var(y - \hat{y})}{Var(y)}1−Var(y)Var(y−y^) | 1.0为完美模型 |
| R²得分 | 1−∑(yi−y^i)2∑(yi−yˉ)21 - \frac{\sum(y_i - \hat{y}_i)^2}{\sum(y_i - \bar{y})^2}1−∑(yi−yˉ)2∑(yi−y^i)2 | 最好为1.0,可为负 |
对应代码:
python
# 回归评估指标
import sklearn.metrics as sm
print("Mean absolute error =", round(sm.mean_absolute_error(y_test, y_test_pred), 2))
print("Mean squared error =", round(sm.mean_squared_error(y_test, y_test_pred), 2))
print("Median absolute error =", round(sm.median_absolute_error(y_test, y_test_pred), 2))
print("Explained variance score =", round(sm.explained_variance_score(y_test, y_test_pred), 2))
print("R2 score =", round(sm.r2_score(y_test, y_test_pred), 2))
1.5 模型保存与加载
对应代码:
python
import cPickle as pickle
# 保存模型
output_model_file = 'saved_model.pkl'
with open(output_model_file, 'w') as f:
pickle.dump(linear_regressor, f)
# 加载模型
with open(output_model_file, 'r') as f:
model_linregr = pickle.load(f)
y_test_pred_new = model_linregr.predict(X_test)
1.6 岭回归(Ridge Regression)
是什么? 在线性回归基础上引入L2正则化的回归方法。
为什么需要? 线性回归对异常值敏感,异常值会显著影响模型。
作用? 通过正则化项控制模型复杂度,降低异常值影响。
目标函数:
min∑(yi−y^i)2+α∑wj2\min \sum(y_i - \hat{y}_i)^2 + \alpha \sum w_j^2min∑(yi−y^i)2+α∑wj2
局限? 只能拟合线性关系,无法捕捉非线性模式。
[由此引出:多项式回归解决非线性拟合问题]
对应代码:
python
# 岭回归示例
ridge_regressor = linear_model.Ridge(alpha=0.01, fit_intercept=True, max_iter=10000)
ridge_regressor.fit(X_train, y_train)
y_test_pred_ridge = ridge_regressor.predict(X_test)
# 评估
print("Mean absolute error =", round(sm.mean_absolute_error(y_test, y_test_pred_ridge), 2))
print("Mean squared error =", round(sm.mean_squared_error(y_test, y_test_pred_ridge), 2))
1.7 多项式回归
是什么? 通过拟合多项式方程来建立非线性关系的回归方法。
为什么需要? 线性回归只能拟合直线,无法捕捉数据中的曲线模式。
作用? 通过增加多项式次数,拟合更复杂的非线性关系。
局限?
- 次数过高会导致过拟合
- 计算复杂度增加
- 需要权衡准确性与计算成本
对应代码:
python
# 多项式回归示例
from sklearn.preprocessing import PolynomialFeatures
# 创建多项式特征(3次)
polynomial = PolynomialFeatures(degree=3)
X_train_transformed = polynomial.fit_transform(X_train)
# 用线性回归拟合多项式特征
poly_linear_model = linear_model.LinearRegression()
poly_linear_model.fit(X_train_transformed, y_train)
# 预测
datapoint = [0.39, 2.78, 7.11]
poly_datapoint = polynomial.fit_transform(datapoint)
print("Polynomial regression:", poly_linear_model.predict(poly_datapoint)[0])
1.8 决策树回归与AdaBoost
是什么?
- 决策树:树状结构的模型,每个节点做决策,叶子节点输出数值
- AdaBoost:自适应增强算法,组合多个弱学习器提高准确性
为什么需要? 单一决策树容易过拟合,AdaBoost通过迭代训练改善效果。
作用?
- 决策树:捕捉特征间的非线性交互
- AdaBoost:提高模型准确性,重点关注难以分类的样本
对应代码:
python
# 决策树回归与AdaBoost
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import AdaBoostRegressor
from sklearn import datasets
from sklearn.metrics import mean_squared_error, explained_variance_score
from sklearn.utils import shuffle
# 加载波士顿房价数据
housing_data = datasets.load_boston()
X, y = shuffle(housing_data.data, housing_data.target, random_state=7)
# 划分数据集
num_training = int(0.8 * len(X))
X_train, y_train = X[:num_training], y[:num_training]
X_test, y_test = X[num_training:], y[num_training:]
# 决策树回归器
dt_regressor = DecisionTreeRegressor(max_depth=4)
dt_regressor.fit(X_train, y_train)
# AdaBoost增强的决策树
ab_regressor = AdaBoostRegressor(DecisionTreeRegressor(max_depth=4),
n_estimators=400, random_state=7)
ab_regressor.fit(X_train, y_train)
# 评估对比
y_pred_dt = dt_regressor.predict(X_test)
y_pred_ab = ab_regressor.predict(X_test)
print("Decision Tree MSE =", round(mean_squared_error(y_test, y_pred_dt), 2))
print("AdaBoost MSE =", round(mean_squared_error(y_test, y_pred_ab), 2))
1.9 特征重要性分析
是什么? 评估各特征对模型预测贡献度的方法。
作用? 识别关键特征,指导特征选择和模型优化。
对应代码:
python
# 特征重要性可视化
def plot_feature_importances(feature_importances, title, feature_names):
# 标准化重要性值
feature_importances = 100.0 * (feature_importances / max(feature_importances))
# 排序
index_sorted = np.flipud(np.argsort(feature_importances))
pos = np.arange(index_sorted.shape[0]) + 0.5
plt.figure()
plt.bar(pos, feature_importances[index_sorted], align='center')
plt.xticks(pos, feature_names[index_sorted])
plt.ylabel('Relative Importance')
plt.title(title)
plt.show()
# 使用
plot_feature_importances(dt_regressor.feature_importances_,
'Decision Tree regressor', housing_data.feature_names)
plot_feature_importances(ab_regressor.feature_importances_,
'AdaBoost regressor', housing_data.feature_names)
1.10 随机森林回归
是什么? 由多棵决策树组成的集成学习方法,通过平均预测结果改善性能。
为什么需要? 单一决策树容易过拟合,随机森林通过集成降低方差。
作用? 提高预测准确性,评估特征重要性。
对应代码:
python
# 随机森林回归示例
from sklearn.ensemble import RandomForestRegressor
rf_regressor = RandomForestRegressor(n_estimators=1000, max_depth=10,
min_samples_split=1)
rf_regressor.fit(X_train, y_train)
y_pred = rf_regressor.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
evs = explained_variance_score(y_test, y_pred)
print("Mean squared error =", round(mse, 2))
print("Explained variance score =", round(evs, 2))
# 特征重要性
plot_feature_importances(rf_regressor.feature_importances_,
'Random Forest regressor', feature_names)
第2章 创建分类器
核心思想:利用数据的特征将其分成若干类型,与回归不同,分类的输出是离散类别标签。
2.1 简单分类器
是什么? 基于简单规则(如直线方程)将数据分为不同类别的基本分类方法。
对应代码:
python
# 简单分类器示例
import numpy as np
import matplotlib.pyplot as plt
# 创建样本数据
X = np.array([[3,1], [2,5], [1,8], [6,4], [5,2], [3,5], [4,7], [4,-1]])
y = [0, 1, 1, 0, 0, 1, 1, 0]
# 按类别分组
class_0 = np.array([X[i] for i in range(len(X)) if y[i]==0])
class_1 = np.array([X[i] for i in range(len(X)) if y[i]==1])
# 可视化
plt.figure()
plt.scatter(class_0[:,0], class_0[:,1], color='black', marker='s')
plt.scatter(class_1[:,0], class_1[:,1], color='black', marker='x')
# 添加分割线 y = x
line_x = range(10)
line_y = line_x
plt.plot(line_x, line_y, color='black', linewidth=3)
plt.show()
2.2 逻辑回归分类器
是什么? 使用逻辑函数将线性组合映射到概率的分类算法。
核心参数:
solver:求解算法类型C:正则化强度(越小正则化越强)
对应代码:
python
# 逻辑回归分类器
from sklearn import linear_model
X = np.array([[4, 7], [3.5, 8], [3.1, 6.2], [0.5, 1], [1, 2],
[1.2, 1.9], [6, 2], [5.7, 1.5], [5.4, 2.2]])
y = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2])
classifier = linear_model.LogisticRegression(solver='liblinear', C=100)
classifier.fit(X, y)
# 可视化分类边界
def plot_classifier(classifier, X, y):
x_min, x_max = min(X[:, 0]) - 1.0, max(X[:, 0]) + 1.0
y_min, y_max = min(X[:, 1]) - 1.0, max(X[:, 1]) + 1.0
step_size = 0.01
x_values, y_values = np.meshgrid(np.arange(x_min, x_max, step_size),
np.arange(y_min, y_max, step_size))
mesh_output = classifier.predict(np.c_[x_values.ravel(), y_values.ravel()])
mesh_output = mesh_output.reshape(x_values.shape)
plt.figure()
plt.pcolormesh(x_values, y_values, mesh_output, cmap=plt.cm.gray)
plt.scatter(X[:, 0], X[:, 1], c=y, s=80, edgecolors='black',
linewidth=1, cmap=plt.cm.Paired)
plt.xlim(x_values.min(), x_values.max())
plt.ylim(y_values.min(), y_values.max())
plt.show()
plot_classifier(classifier, X, y)
2.3 朴素贝叶斯分类器
是什么? 基于贝叶斯定理的概率分类器,假设特征之间相互独立。
对应代码:
python
# 朴素贝叶斯分类器
from sklearn.naive_bayes import GaussianNB
input_file = 'data_multivar.txt'
X = []
y = []
with open(input_file, 'r') as f:
for line in f.readlines():
data = [float(x) for x in line.split(',')]
X.append(data[:-1])
y.append(data[-1])
X = np.array(X)
y = np.array(y)
classifier_gaussiannb = GaussianNB()
classifier_gaussiannb.fit(X, y)
y_pred = classifier_gaussiannb.predict(X)
accuracy = 100.0 * (y == y_pred).sum() / X.shape[0]
print("Accuracy of the classifier =", round(accuracy, 2), "%")
2.4 数据集划分
是什么? 将数据分为训练集和测试集,用训练集训练模型,用测试集评估模型。
为什么需要? 避免在训练数据上过拟合,评估模型在未知数据上的泛化能力。
对应代码:
python
# 数据集划分
from sklearn import cross_validation
X_train, X_test, y_train, y_test = cross_validation.train_test_split(
X, y, test_size=0.25, random_state=5)
classifier_gaussiannb_new = GaussianNB()
classifier_gaussiannb_new.fit(X_train, y_train)
y_test_pred = classifier_gaussiannb_new.predict(X_test)
accuracy = 100.0 * (y_test == y_test_pred).sum() / X_test.shape[0]
print("Accuracy of the classifier =", round(accuracy, 2), "%")
2.5 交叉验证
是什么? 用数据集的不同子集反复训练和验证模型的方法。
为什么需要? 单一的训练/测试划分可能因数据划分方式导致评估偏差。
作用? 更稳定地评估模型性能,减少过拟合风险。
性能指标:
| 指标 | 定义 | 计算方式 |
|---|---|---|
| 精度(Precision) | 正确分类数/总分类数 | TP/(TP+FP) |
| 召回率(Recall) | 正确分类数/该类总数 | TP/(TP+FN) |
| F1得分 | 精度和召回率的调和均值 | 2×P×R/(P+R) |
对应代码:
python
# 交叉验证
num_validations = 5
accuracy = cross_validation.cross_val_score(classifier_gaussiannb,
X, y, scoring='accuracy', cv=num_validations)
print("Accuracy:", str(round(100*accuracy.mean(), 2)) + "%")
f1 = cross_validation.cross_val_score(classifier_gaussiannb,
X, y, scoring='f1_weighted', cv=num_validations)
print("F1:", str(round(100*f1.mean(), 2)) + "%")
precision = cross_validation.cross_val_score(classifier_gaussiannb,
X, y, scoring='precision_weighted', cv=num_validations)
print("Precision:", str(round(100*precision.mean(), 2)) + "%")
recall = cross_validation.cross_val_score(classifier_gaussiannb,
X, y, scoring='recall_weighted', cv=num_validations)
print("Recall:", str(round(100*recall.mean(), 2)) + "%")
2.6 混淆矩阵
是什么? 展示分类模型预测结果与实际标签对比的表格。
作用? 直观展示分类错误的类型和数量,帮助诊断模型问题。
对应代码:
python
# 混淆矩阵
from sklearn.metrics import confusion_matrix
y_true = [1, 0, 0, 2, 1, 0, 3, 3, 3]
y_pred = [1, 1, 0, 2, 1, 0, 1, 3, 3]
confusion_mat = confusion_matrix(y_true, y_pred)
def plot_confusion_matrix(confusion_mat):
plt.imshow(confusion_mat, interpolation='nearest', cmap=plt.cm.Paired)
plt.title('Confusion matrix')
plt.colorbar()
tick_marks = np.arange(4)
plt.xticks(tick_marks, tick_marks)
plt.yticks(tick_marks, tick_marks)
plt.ylabel('True label')
plt.xlabel('Predicted label')
plt.show()
plot_confusion_matrix(confusion_mat)
2.7 性能报告
对应代码:
python
# 性能报告
from sklearn.metrics import classification_report
y_true = [1, 0, 0, 2, 1, 0, 3, 3, 3]
y_pred = [1, 1, 0, 2, 1, 0, 1, 3, 3]
target_names = ['Class-0', 'Class-1', 'Class-2', 'Class-3']
print(classification_report(y_true, y_pred, target_names=target_names))
2.8 随机森林分类器
对应代码:
python
# 随机森林分类器
from sklearn.ensemble import RandomForestClassifier
params = {'n_estimators': 200, 'max_depth': 8, 'random_state': 7}
classifier = RandomForestClassifier(**params)
classifier.fit(X, y)
# 交叉验证
accuracy = cross_validation.cross_val_score(classifier,
X, y, scoring='accuracy', cv=3)
print("Accuracy of the classifier:", str(round(100*accuracy.mean(), 2)) + "%")
2.9 验证曲线
是什么? 展示超参数变化对训练得分和验证得分影响的曲线。
作用? 帮助选择最优超参数,诊断过拟合/欠拟合。
对应代码:
python
# 验证曲线
from sklearn.learning_curve import validation_curve
classifier = RandomForestClassifier(max_depth=4, random_state=7)
parameter_grid = np.linspace(25, 200, 8).astype(int)
train_scores, validation_scores = validation_curve(
classifier, X, y, "n_estimators", parameter_grid, cv=5)
# 绘制曲线
plt.figure()
plt.plot(parameter_grid, 100*np.average(train_scores, axis=1), color='black')
plt.title('Training curve')
plt.xlabel('Number of estimators')
plt.ylabel('Accuracy')
plt.show()
2.10 学习曲线
是什么? 展示训练数据集大小对模型性能影响的曲线。
作用? 帮助确定合适的训练数据规模,平衡计算成本与模型性能。
对应代码:
python
# 学习曲线
from sklearn.learning_curve import learning_curve
classifier = RandomForestClassifier(random_state=7)
parameter_grid = np.array([200, 500, 800, 1100])
train_sizes, train_scores, validation_scores = learning_curve(
classifier, X, y, train_sizes=parameter_grid, cv=5)
plt.figure()
plt.plot(parameter_grid, 100*np.average(train_scores, axis=1), color='black')
plt.title('Learning curve')
plt.xlabel('Number of training samples')
plt.ylabel('Accuracy')
plt.show()
第3章 预测建模(SVM)
核心思想:通过寻找最优分割边界,将数据点划分到不同类别,支持线性和非线性分类。
3.1 SVM线性分类器
是什么? 使用线性核函数的支持向量机,寻找最优线性分割边界。
对应代码:
python
# SVM线性分类器
from sklearn import cross_validation
from sklearn.svm import SVC
X_train, X_test, y_train, y_test = cross_validation.train_test_split(
X, y, test_size=0.25, random_state=5)
params = {'kernel': 'linear'}
classifier = SVC(**params)
classifier.fit(X_train, y_train)
# 预测与评估
y_test_pred = classifier.predict(X_test)
print(classification_report(y_test, y_test_pred,
target_names=['Class-' + str(int(i)) for i in set(y)]))
3.2 SVM非线性分类器
是什么? 使用非线性核函数(多项式、RBF)的SVM,可处理非线性可分数据。
核函数对比:
| 核函数 | 参数 | 适用场景 |
|---|---|---|
| 线性 | 无 | 线性可分数据 |
| 多项式 | degree | 有明确多项式边界的数据 |
| RBF | gamma | 复杂非线性边界 |
对应代码:
python
# 多项式核SVM
params = {'kernel': 'poly', 'degree': 3}
classifier = SVC(**params)
classifier.fit(X_train, y_train)
# RBF核SVM
params = {'kernel': 'rbf'}
classifier = SVC(**params)
classifier.fit(X_train, y_train)
3.3 类别不平衡问题
是什么? 训练数据中不同类别的样本数量差异很大。
为什么需要处理? 分类器会偏向样本多的类别,导致少数类识别率低。
解决方案: 使用class_weight='auto'自动调整权重。
对应代码:
python
# 处理类别不平衡
params = {'kernel': 'linear', 'class_weight': 'auto'}
classifier = SVC(**params)
classifier.fit(X_train, y_train)
3.4 置信度提取
对应代码:
python
# 提取置信度
params = {'kernel': 'rbf', 'probability': True}
classifier = SVC(**params)
classifier.fit(X_train, y_train)
# 预测概率
input_datapoints = np.array([[2, 1.5], [8, 9], [4.8, 5.2]])
print("Confidence measure:")
for i in input_datapoints:
print(i, '-->', classifier.predict_proba(i)[0])
3.5 网格搜索最优超参数
对应代码:
python
# 网格搜索最优超参数
from sklearn import grid_search
parameter_grid = [
{'kernel': ['linear'], 'C': [1, 10, 50, 600]},
{'kernel': ['poly'], 'degree': [2, 3]},
{'kernel': ['rbf'], 'gamma': [0.01, 0.001], 'C': [1, 10, 50, 600]},
]
metrics = ['precision', 'recall_weighted']
for metric in metrics:
classifier = grid_search.GridSearchCV(
SVC(C=1), parameter_grid, cv=5, scoring=metric)
classifier.fit(X_train, y_train)
print("Best params:", classifier.best_params_)
3.6 SVM回归(SVR)
对应代码:
python
# SVM回归
from sklearn.svm import SVR
params = {'kernel': 'rbf', 'C': 10.0, 'epsilon': 0.2}
regressor = SVR(**params)
regressor.fit(X, y)
y_pred = regressor.predict(X)
print("Mean absolute error =", round(sm.mean_absolute_error(y, y_pred), 2))
第4章 无监督学习------聚类
核心思想:在没有标记的数据中发现内在结构和模式,将相似数据点分组。
4.1 K-Means聚类
是什么? 将数据划分为K个簇,使簇内距离平方和最小化的迭代算法。
目标函数:
min∑i=1k∑x∈Ci∥x−μi∥2\min \sum_{i=1}^{k} \sum_{x \in C_i} \|x - \mu_i\|^2min∑i=1k∑x∈Ci∥x−μi∥2
对应代码:
python
# K-Means聚类
from sklearn.cluster import KMeans
num_clusters = 4
kmeans = KMeans(init='k-means++', n_clusters=num_clusters, n_init=10)
kmeans.fit(data)
# 获取标签和中心点
labels = kmeans.labels_
centroids = kmeans.cluster_centers_
4.2 矢量量化(图像压缩)
是什么? 将N维数据"四舍五入"到最近的中心点,用于图像压缩。
对应代码:
python
# 矢量量化压缩图像
from sklearn import cluster
def compress_image(img, num_clusters):
X = img.reshape((-1, 1))
kmeans = cluster.KMeans(n_clusters=num_clusters, n_init=4, random_state=5)
kmeans.fit(X)
centroids = kmeans.cluster_centers_.squeeze()
labels = kmeans.labels_
input_image_compressed = np.choose(labels, centroids).reshape(img.shape)
return input_image_compressed
4.3 均值漂移聚类
是什么? 不需要预先指定簇数量的聚类算法,自动发现数据中的模式。
优点: 自动确定簇数量
对应代码:
python
# 均值漂移聚类
from sklearn.cluster import MeanShift, estimate_bandwidth
bandwidth = estimate_bandwidth(X, quantile=0.1, n_samples=len(X))
meanshift_estimator = MeanShift(bandwidth=bandwidth, bin_seeding=True)
meanshift_estimator.fit(X)
labels = meanshift_estimator.labels_
centroids = meanshift_estimator.cluster_centers_
num_clusters = len(np.unique(labels))
4.4 凝聚层次聚类
是什么? 自底向上合并簇的层次聚类方法。
对应代码:
python
# 凝聚层次聚类
from sklearn.cluster import AgglomerativeClustering
from sklearn.neighbors import kneighbors_graph
def perform_clustering(X, connectivity, title, num_clusters=3, linkage='ward'):
model = AgglomerativeClustering(linkage=linkage,
connectivity=connectivity,
n_clusters=num_clusters)
model.fit(X)
labels = model.labels_
# 可视化...
# 使用K近邻连接
connectivity = kneighbors_graph(X, 10, include_self=False)
perform_clustering(X, connectivity, 'K-Neighbors connectivity')
4.5 聚类效果评估(轮廓系数)
轮廓系数公式:
s=b−amax(a,b)s = \frac{b - a}{\max(a, b)}s=max(a,b)b−a
其中a是样本与同簇其他点的平均距离,b是样本与最近簇所有点的平均距离
对应代码:
python
# 轮廓系数评估
from sklearn import metrics
scores = []
range_values = np.arange(2, 10)
for i in range_values:
kmeans = KMeans(init='k-means++', n_clusters=i, n_init=10)
kmeans.fit(data)
score = metrics.silhouette_score(data, kmeans.labels_,
metric='euclidean',
sample_size=len(data))
scores.append(score)
# 选择轮廓系数最高的K值
best_k = range_values[np.argmax(scores)]
4.6 DBSCAN聚类
是什么? 基于密度的聚类算法,自动发现簇数量,能识别异常点。
核心参数:
eps:邻域半径min_samples:核心点所需最小样本数
对应代码:
python
# DBSCAN聚类
from sklearn.cluster import DBSCAN
# 搜索最优eps
eps_grid = np.linspace(0.3, 1.2, num=10)
silhouette_scores = []
for eps in eps_grid:
model = DBSCAN(eps=eps, min_samples=5).fit(X)
labels = model.labels_
silhouette_score = metrics.silhouette_score(X, labels)
silhouette_scores.append(silhouette_score)
best_eps = eps_grid[np.argmax(silhouette_scores)]
第5章 构建推荐引擎
核心思想:通过协同过滤或基于内容的过滤,预测用户兴趣并推荐相关内容。
5.1 欧氏距离分数
公式:
score=11+∑(xi−yi)2score = \frac{1}{1 + \sqrt{\sum(x_i - y_i)^2}}score=1+∑(xi−yi)2 1
对应代码:
python
# 欧氏距离分数
def euclidean_score(dataset, user1, user2):
if user1 not in dataset or user2 not in dataset:
raise TypeError('User not present in the dataset')
# 提取共同评分的电影
rated_by_both = {}
for item in dataset[user1]:
if item in dataset[user2]:
rated_by_both[item] = 1
if len(rated_by_both) == 0:
return 0
squared_differences = []
for item in rated_by_both:
squared_differences.append(np.square(
dataset[user1][item] - dataset[user2][item]))
return 1 / (1 + np.sqrt(np.sum(squared_differences)))
5.2 皮尔逊相关系数
公式:
r=∑(xi−xˉ)(yi−yˉ)∑(xi−xˉ)2∑(yi−yˉ)2r = \frac{\sum(x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum(x_i - \bar{x})^2 \sum(y_i - \bar{y})^2}}r=∑(xi−xˉ)2∑(yi−yˉ)2 ∑(xi−xˉ)(yi−yˉ)
对应代码:
python
# 皮尔逊相关系数
def pearson_score(dataset, user1, user2):
if user1 not in dataset or user2 not in dataset:
raise TypeError('User not present in the dataset')
rated_by_both = {}
for item in dataset[user1]:
if item in dataset[user2]:
rated_by_both[item] = 1
num_ratings = len(rated_by_both)
if num_ratings == 0:
return 0
user1_sum = np.sum([dataset[user1][item] for item in rated_by_both])
user2_sum = np.sum([dataset[user2][item] for item in rated_by_both])
user1_squared_sum = np.sum([np.square(dataset[user1][item])
for item in rated_by_both])
user2_squared_sum = np.sum([np.square(dataset[user2][item])
for item in rated_by_both])
product_sum = np.sum([dataset[user1][item] * dataset[user2][item]
for item in rated_by_both])
Sxy = product_sum - (user1_sum * user2_sum / num_ratings)
Sxx = user1_squared_sum - np.square(user1_sum) / num_ratings
Syy = user2_squared_sum - np.square(user2_sum) / num_ratings
if Sxx * Syy == 0:
return 0
return Sxy / np.sqrt(Sxx * Syy)
5.3 电影推荐系统
对应代码:
python
# 电影推荐系统
def generate_recommendations(dataset, user):
if user not in dataset:
raise TypeError('User not present in the dataset')
total_scores = {}
similarity_sums = {}
for u in [x for x in dataset if x != user]:
similarity_score = pearson_score(dataset, user, u)
if similarity_score <= 0:
continue
for item in [x for x in dataset[u]
if x not in dataset[user] or dataset[user][x] == 0]:
total_scores.update({item: dataset[u][item] * similarity_score})
similarity_sums.update({item: similarity_score})
if len(total_scores) == 0:
return ['No recommendations possible']
# 生成标准化评分列表
movie_ranks = np.array([[total/similarity_sums[item], item]
for item, total in total_scores.items()])
movie_ranks = movie_ranks[np.argsort(movie_ranks[:, 0])[::-1]]
recommendations = [movie for _, movie in movie_ranks]
return recommendations
第6章 分析文本数据
核心思想:将非结构化文本转换为结构化表示,使机器学习算法能够处理和理解文本内容。
6.1 文本预处理
| 技术 | 作用 | 示例 |
|---|---|---|
| 标记解析(Tokenization) | 将文本分割为单词/句子 | "Hello world" → ["Hello", "world"] |
| 词干提取(Stemming) | 提取单词词干 | playing → play |
| 词形还原(Lemmatization) | 还原单词基本形式 | wolves → wolf |
| 停用词去除 | 去除常见无意义词 | the, is, and |
对应代码:
python
# 文本预处理
from nltk.tokenize import sent_tokenize, word_tokenize
from nltk.stem.porter import PorterStemmer
from nltk.stem import WordNetLemmatizer
# 句子解析
text = "Are you curious about tokenization? Let's see how it works!"
sent_tokenize_list = sent_tokenize(text)
# 单词解析
words = word_tokenize(text)
# 词干提取
stemmer = PorterStemmer()
stemmed = [stemmer.stem(word) for word in words]
# 词形还原
lemmatizer = WordNetLemmatizer()
lemmatized = [lemmatizer.lemmatize(word, pos='v') for word in words]
6.2 词袋模型与TF-IDF
TF-IDF公式:
TF−IDF(t,d)=TF(t,d)×IDF(t)TF-IDF(t, d) = TF(t, d) \times IDF(t)TF−IDF(t,d)=TF(t,d)×IDF(t)
IDF(t)=logNDF(t)IDF(t) = \log\frac{N}{DF(t)}IDF(t)=logDF(t)N
对应代码:
python
# TF-IDF文本分类
from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer
from sklearn.naive_bayes import MultinomialNB
# 特征提取
vectorizer = CountVectorizer()
X_train_termcounts = vectorizer.fit_transform(training_data.data)
# TF-IDF变换
tfidf_transformer = TfidfTransformer()
X_train_tfidf = tfidf_transformer.fit_transform(X_train_termcounts)
# 训练分类器
classifier = MultinomialNB().fit(X_train_tfidf, training_data.target)
# 预测
X_input_termcounts = vectorizer.transform(input_data)
X_input_tfidf = tfidf_transformer.transform(X_input_termcounts)
predicted_categories = classifier.predict(X_input_tfidf)
6.3 情感分析
对应代码:
python
# 情感分析
from nltk.classify import NaiveBayesClassifier
from nltk.corpus import movie_reviews
def extract_features(word_list):
return dict([(word, True) for word in word_list])
# 加载数据
positive_fileids = movie_reviews.fileids('pos')
negative_fileids = movie_reviews.fileids('neg')
features_positive = [(extract_features(movie_reviews.words(fileids=[f])), 'Positive')
for f in positive_fileids]
features_negative = [(extract_features(movie_reviews.words(fileids=[f])), 'Negative')
for f in negative_fileids]
# 划分训练集和测试集
threshold_factor = 0.8
threshold_positive = int(threshold_factor * len(features_positive))
threshold_negative = int(threshold_factor * len(features_negative))
features_train = features_positive[:threshold_positive] + features_negative[:threshold_negative]
features_test = features_positive[threshold_positive:] + features_negative[threshold_negative:]
# 训练分类器
classifier = NaiveBayesClassifier.train(features_train)
accuracy = nltk.classify.util.accuracy(classifier, features_test)
6.4 主题建模(LDA)
对应代码:
python
# LDA主题建模
from gensim import models, corpora
from nltk.corpus import stopwords
# 预处理
class Preprocessor(object):
def __init__(self):
self.tokenizer = RegexpTokenizer(r'\w+')
self.stop_words_english = stopwords.words('english')
self.stemmer = SnowballStemmer('english')
def process(self, input_text):
tokens = self.tokenizer.tokenize(input_text.lower())
tokens_stopwords = [x for x in tokens if x not in self.stop_words_english]
tokens_stemmed = [self.stemmer.stem(x) for x in tokens_stopwords]
return tokens_stemmed
# 创建词典和语料库
preprocessor = Preprocessor()
processed_tokens = [preprocessor.process(x) for x in data]
dict_tokens = corpora.Dictionary(processed_tokens)
corpus = [dict_tokens.doc2bow(text) for text in processed_tokens]
# LDA模型
num_topics = 2
ldamodel = models.ldamodel.LdaModel(corpus, num_topics=num_topics,
id2word=dict_tokens, passes=25)
第7章 语音识别
核心思想:将音频信号转换为频域特征,使用隐马尔科夫模型建模时间序列模式。
7.1 音频处理基础
对应代码:
python
# 读取和绘制音频
from scipy.io import wavfile
sampling_freq, audio = wavfile.read('input_read.wav')
audio = audio / (2.**15) # 标准化
# 提取前30个值
audio = audio[:30]
x_values = np.arange(0, len(audio), 1) / float(sampling_freq) * 1000
plt.plot(x_values, audio, color='black')
plt.xlabel('Time (ms)')
plt.ylabel('Amplitude')
plt.show()
7.2 频域转换与MFCC特征
对应代码:
python
# 频域转换
from scipy.io import wavfile
import numpy as np
sampling_freq, audio = wavfile.read('input_freq.wav')
audio = audio / (2.**15)
# 傅里叶变换
transformed_signal = np.fft.fft(audio)
half_length = np.ceil((len(audio) + 1) / 2.0)
transformed_signal = abs(transformed_signal[0:half_length])
transformed_signal /= float(len(audio))
transformed_signal **= 2
# 功率信号
power = 10 * np.log10(transformed_signal)
# MFCC特征
from features import mfcc, logfbank
mfcc_features = mfcc(audio, sampling_freq)
filterbank_features = logfbank(audio, sampling_freq)
7.3 隐马尔科夫模型(HMM)
对应代码:
python
# HMM训练器
from hmmlearn import hmm
class HMMTrainer(object):
def __init__(self, model_name='GaussianHMM', n_components=4,
cov_type='diag', n_iter=1000):
self.model_name = model_name
self.n_components = n_components
self.cov_type = cov_type
self.n_iter = n_iter
if self.model_name == 'GaussianHMM':
self.model = hmm.GaussianHMM(n_components=self.n_components,
covariance_type=self.cov_type,
n_iter=self.n_iter)
else:
raise TypeError('Invalid model type')
def train(self, X):
np.seterr(all='ignore')
self.models.append(self.model.fit(X))
def get_score(self, input_data):
return self.model.score(input_data)
7.4 语音识别器
对应代码:
python
# 语音识别器
hmm_models = []
# 为每个单词训练HMM
for dirname in os.listdir(input_folder):
subfolder = os.path.join(input_folder, dirname)
if not os.path.isdir(subfolder):
continue
label = subfolder[subfolder.rfind('/') + 1:]
X = np.array([])
for filename in [x for x in os.listdir(subfolder) if x.endswith('.wav')][:-1]:
filepath = os.path.join(subfolder, filename)
sampling_freq, audio = wavfile.read(filepath)
mfcc_features = mfcc(audio, sampling_freq)
if len(X) == 0:
X = mfcc_features
else:
X = np.append(X, mfcc_features, axis=0)
hmm_trainer = HMMTrainer()
hmm_trainer.train(X)
hmm_models.append((hmm_trainer, label))
# 预测
for input_file in input_files:
sampling_freq, audio = wavfile.read(input_file)
mfcc_features = mfcc(audio, sampling_freq)
max_score = None
output_label = None
for item in hmm_models:
hmm_model, label = item
score = hmm_model.get_score(mfcc_features)
if score > max_score:
max_score = score
output_label = label
print("Predicted:", output_label)
第8章 时间序列和时序数据
核心思想:时间序列数据的顺序至关重要,需要建立能够捕捉时间依赖关系的模型。
8.1 时间序列数据处理
对应代码:
python
# 时间序列数据处理
import pandas as pd
def convert_data_to_timeseries(input_file, column, verbose=False):
data = np.loadtxt(input_file, delimiter=',')
start_date = str(int(data[0,0])) + '-' + str(int(data[0,1]))
end_date = str(int(data[-1,0] + 1)) + '-' + str(int(data[-1,1]% 12 + 1))
dates = pd.date_range(start_date, end_date, freq='M')
data_timeseries = pd.Series(data[:,column], index=dates)
return data_timeseries
# 使用
data_timeseries = convert_data_to_timeseries('data_timeseries.txt', 2)
data_timeseries.plot()
plt.show()
8.2 时间序列统计
对应代码:
python
# 时间序列统计
dataframe = pd.DataFrame({'first': data1, 'second': data2})
# 基本统计
print('Maximum:', dataframe.max())
print('Minimum:', dataframe.min())
print('Mean:', dataframe.mean())
# 滑动均值
pd.rolling_mean(dataframe, window=24).plot()
# 相关性
print('Correlation:', dataframe.corr())
# 滑动相关性
pd.rolling_corr(dataframe['first'], dataframe['second'], window=60).plot()
8.3 HMM用于时间序列
对应代码:
python
# HMM用于时间序列
from hmmlearn.hmm import GaussianHMM
# 加载数据
input_file = 'data_hmm.txt'
data = np.loadtxt(input_file, delimiter=',')
X = np.column_stack([data[:,2]])
# 创建并训练HMM
num_components = 4
model = GaussianHMM(n_components=num_components, covariance_type="diag",
n_iter=1000)
model.fit(X)
# 预测隐藏状态
hidden_states = model.predict(X)
# 生成数据
num_samples = 1000
samples, _ = model.sample(num_samples)
plt.plot(np.arange(num_samples), samples[:,0], c='black')
plt.show()
8.4 条件随机场(CRF)
是什么? 判别式概率模型,不假设输出独立性,适合序列标注任务。
与HMM对比:
| 特性 | HMM | CRF |
|---|---|---|
| 类型 | 生成模型 | 判别模型 |
| 假设 | 输出独立性 | 无独立性假设 |
| 应用 | 语音识别 | 序列标注 |
对应代码:
python
# 条件随机场
from pystruct.models import ChainCRF
from pystruct.learners import FrankWolfeSSVM
class CRFTrainer(object):
def __init__(self, c_value, classifier_name='ChainCRF'):
self.c_value = c_value
if classifier_name == 'ChainCRF':
model = ChainCRF()
self.clf = FrankWolfeSSVM(model=model, C=self.c_value, max_iter=50)
def train(self, X_train, y_train):
self.clf.fit(X_train, y_train)
def evaluate(self, X_test, y_test):
return self.clf.score(X_test, y_test)
def classify(self, input_data):
return self.clf.predict(input_data)[0]
第9章 图像内容分析
核心思想:使用计算机视觉技术提取图像特征,构建目标识别系统。
9.1 边缘检测
对应代码:
python
# 边缘检测
import cv2
img = cv2.imread('chair.jpg', cv2.IMREAD_GRAYSCALE)
# Sobel算子
sobel_horizontal = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)
sobel_vertical = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5)
# Laplacian
laplacian = cv2.Laplacian(img, cv2.CV_64F)
# Canny边缘检测
canny = cv2.Canny(img, 50, 240)
9.2 SIFT特征检测
对应代码:
python
# SIFT特征检测
img = cv2.imread('table.jpg')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sift = cv2.xfeatures2d.SIFT_create()
keypoints = sift.detect(img_gray, None)
# 绘制关键点
img_sift = np.copy(img)
cv2.drawKeypoints(img, keypoints, img_sift,
flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
9.3 视觉码本与图像分类
对应代码:
python
# 视觉码本
class BagOfWords(object):
def __init__(self, num_clusters=32):
self.num_dims = 128
self.num_clusters = num_clusters
def cluster(self, datapoints):
kmeans = KMeans(self.num_clusters, n_init=10, max_iter=10, tol=1.0)
res = kmeans.fit(datapoints)
centroids = res.cluster_centers_
return kmeans, centroids
def construct_feature(self, img, kmeans, centroids):
keypoints = StarFeatureDetector().detect(img)
keypoints, feature_vectors = compute_sift_features(img, keypoints)
labels = kmeans.predict(feature_vectors)
feature_vector = np.zeros(self.num_clusters)
for i, item in enumerate(feature_vectors):
feature_vector[labels[i]] += 1
return self.normalize(feature_vector)
第10章 人脸识别
核心思想:从图像中提取人脸特征,使用降维和分类技术实现人脸识别。
10.1 Haar级联人脸检测
对应代码:
python
# Haar级联人脸检测
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_alt.xml')
cap = cv2.VideoCapture(0)
scaling_factor = 0.5
while True:
ret, frame = cap.read()
frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
face_rects = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in face_rects:
cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0), 3)
cv2.imshow('Face Detector', frame)
if cv2.waitKey(1) == 27:
break
cap.release()
cv2.destroyAllWindows()
10.2 主成分分析(PCA)
对应代码:
python
# PCA降维
from sklearn import decomposition
pca = decomposition.PCA()
pca.fit(X)
# 打印方差
variances = pca.explained_variance_
print('Variances:', variances)
# 选择重要维度
thresh_variance = 0.8
num_useful_dims = len(np.where(variances > thresh_variance)[0])
pca.n_components = num_useful_dims
X_new = pca.fit_transform(X)
10.3 核主成分分析(KPCA)
对应代码:
python
# KPCA
from sklearn.decomposition import PCA, KernelPCA
# 生成同心圆数据
X, y = make_circles(n_samples=500, factor=0.2, noise=0.04)
# 普通PCA
pca = PCA()
X_pca = pca.fit_transform(X)
# KPCA
kernel_pca = KernelPCA(kernel="rbf", fit_inverse_transform=True, gamma=10)
X_kernel_pca = kernel_pca.fit_transform(X)
10.4 局部二值模式直方图(LBPH)人脸识别
对应代码:
python
# LBPH人脸识别
recognizer = cv2.face.createLBPHFaceRecognizer()
# 训练
recognizer.train(images, np.array(labels))
# 预测
predicted_index, conf = recognizer.predict(predict_image[y:y+h, x:x+w])
predicted_person = le.num_to_word(predicted_index)
第11章 深度神经网络
核心思想:通过多层非线性变换学习数据的层次化表示,使用反向传播算法优化网络参数。
11.1 感知器
对应代码:
python
# 感知器
import neurolab as nl
data = np.array([[0.3, 0.2], [0.1, 0.4], [0.4, 0.6], [0.9, 0.5]])
labels = np.array([[0], [0], [0], [1]])
# 创建感知器
perceptron = nl.net.newp([[0, 1],[0, 1]], 1)
# 训练
error = perceptron.train(data, labels, epochs=50, show=15, lr=0.01)
11.2 深度神经网络
对应代码:
python
# 深度神经网络
import neurolab as nl
# 生成数据
min_value = -12
max_value = 12
num_datapoints = 90
x = np.linspace(min_value, max_value, num_datapoints)
y = 2 * np.square(x) + 7
y /= np.linalg.norm(y)
data = x.reshape(num_datapoints, 1)
labels = y.reshape(num_datapoints, 1)
# 定义网络(2个隐藏层,每层10个神经元)
multilayer_net = nl.net.newff([[min_value, max_value]], [10, 10, 1])
multilayer_net.trainf = nl.train.train_gd
# 训练
error = multilayer_net.train(data, labels, epochs=800, show=100, goal=0.01)
# 预测
predicted_output = multilayer_net.sim(data)
11.3 递归神经网络(RNN)
对应代码:
python
# 递归神经网络
net = nl.net.newelm([[-2, 2]], [10, 1], [nl.trans.TanSig(), nl.trans.PureLin()])
# 初始化
net.layers[0].initf = nl.init.InitRand([-0.1, 0.1], 'wb')
net.layers[1].initf = nl.init.InitRand([-0.1, 0.1], 'wb')
net.init()
# 训练
error = net.train(data, amplitude, epochs=1000, show=100, goal=0.01)
output = net.sim(data)
第12章 数据可视化
核心思想:使用图形和图表直观表达数据,帮助理解数据分布、趋势和关系。
12.1 常用可视化图表
| 图表类型 | 适用场景 | 代码 |
|---|---|---|
| 3D散点图 | 三维数据分布 | ax.scatter(x, y, z) |
| 气泡图 | 展示第三个维度(大小) | plt.scatter(x, y, s=area) |
| 饼图 | 比例分布 | plt.pie(values) |
| 直方图 | 数据分布对比 | plt.bar(x, height) |
| 热力图 | 矩阵数据可视化 | ax.pcolor(data) |
对应代码:
python
# 3D散点图
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x_vals, y_vals, z_vals, c='k', marker='o')
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
plt.show()
# 气泡图
x = np.random.rand(num_vals)
y = np.random.rand(num_vals)
area = np.pi * (max_radius * np.random.rand(num_vals)) ** 2
colors = np.random.rand(num_vals)
plt.scatter(x, y, s=area, c=colors, alpha=1.0)
plt.show()
# 热力图
data = np.random.rand(5, 5)
heatmap = ax.pcolor(data, cmap=plt.cm.gray)
plt.show()
附录:常用库速查
| 库 | 用途 |
|---|---|
| NumPy | 数值计算、数组操作 |
| SciPy | 科学计算、信号处理 |
| scikit-learn | 机器学习算法 |
| matplotlib | 数据可视化 |
| NLTK | 自然语言处理 |
| OpenCV | 计算机视觉 |
| pandas | 数据处理与分析 |
| neurolab | 神经网络 |
| hmmlearn | 隐马尔科夫模型 |
| gensim | 主题建模 |
笔记整理完成