传统机器学习
传统机器学习是机器学习的一个分支,主要关注如何使用传统的机器学习算法来解决分类、回归、聚类等问题。
高度依赖于特征工程,需要手动提取特征,然后输入到模型中。
主要使用的框架是scikit-learn、pandas、numpy、matplotlib、opencv等。
分类算法
KNN算法
这个算法既可以解决分类问题,也可以用于回归问题,但工业上用于分类的情况更多。
KNN 先记录所有已知数据,再利用一个距离函数,
找出已知数据中距离未知事件最近的 K 组数据,
最后按照这 K 组数据里最常见的类别预测该事件。
from sklearn.neighbors import KNeighborsClassifier
import numpy as np
# 创建一些示例数据
X = np.array([[1, 2], [2, 3], [2, 5], [3, 2], [3, 3], [4, 5]]) # 特征
y = np.array([0, 0, 1, 0, 1, 1]) # 目标标签
# 创建K-最近邻分类器
k = 3 # 选择K的值
model = KNeighborsClassifier(n_neighbors=k).fit(X, y)
# 预测新数据点
new_data_point = np.array([[3, 4]]) # 要预测的新数据点
# .predicts()方法返回一个数组,数组中包含了预测的类别
predicted_class = model.predict(new_data_point)
print("预测类别:", predicted_class)
简单实战
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
# 导入鸢尾花数据库
from sklearn.datasets import load_iris
# 加载数据集,数据集包含数据的特征、标签、类别等许多信息
iris = load_iris()
# 获取数据特征(即花的高度、宽度等)
iris_X = iris.data
# 获取数据标签(即花的品种,用0、1、2代替)
iris_y = iris.target
print(len(iris_X)) # 查看数据集的大小
# # 获取前2条数据,从0开始到2结束,不包括2。写法1
# print(iris_X[0:2])
# # 获取前2条数据,从0开始到2结束,不包括2。写法2,省略0
# print(iris_X[:2])
# # 获取前2条数据,从0开始到2结束,不包括2。写法3,省略0
# print(iris_X[:2,:])
# # 获取前2条数据,从0开始到2结束,不包括2。写法4,省略0,只取第一列
# print(iris_X[:2,0])
# # 查看花的类别
# print(iris_y)
# # 查看花的数据
# print(iris_X)
# # 合在一起查看
# print(list(zip(iris_X,iris_y)))
# 把数据打乱,并分成测试数据和训练数据,test_size是测试数据的比例,0.3表示为30%
X_train, X_test, y_train, y_test = train_test_split(iris_X, iris_y, test_size=0.3)
'''
train_test_split详解
此方法会将数据和标签均分成两部分并打乱,一部分用于训练,一部分用于测试。
所以返回的数据有4个,我们用1、2、3、4给他们做上记号.
数据X [------70%---(1)-- | -30%(2)-]
标签y [------70%---(3)-- | -30%(4)-]
与上图对应,依次是:
训练的数据X(1), 测试的数据X(2),
↑↓ ↑↓
训练的标签y(3), 测试的标签y(4)
用(1)、(3)喂出一个模型
让模型预测(2),获得预测结果
将预测结果与(4)进行比较来测试模型的准确率
'''
# 查看训练数据,已经被随机打乱了
# print(y_train)
# 实例化KNN分类器
knn = KNeighborsClassifier()
# .fit()方法用于训练模型,即让模型从数据中学习
knn.fit(X_train, y_train)
# .predicts()方法返回一个数组,数组中包含了预测的类别
print(knn.predict(X_test))
# 查看真实数据
print(y_test)
效果评估
right = 0
error = 0
for i in zip(knn.predict(X_test),y_test):
#print(i)
if i[0] == i[1]:
right +=1
else:
error +=1
print(right,error)
print('正确率:{}%'.format(right/(right+error)*100))
效果评估的改进
print('正确率:{}%'.format(knn.score(X_test,y_test)*100))
# 正确率:100.0%
实时分类器
描述
KNN 算法先记录所有已知数据,再利用一个距离函数,找出已知数据中距离未知事件最近的 K 组数据,最后按照这 K 组数据里最常见的类别预测该事件。可以解决分类问题。
请编写一段程序读取用户的摄像头,让用户通过按键或点击的方式实时训练并查看当前摄像头的预测结果。
题解
'''
新建`.py`并将下方代码复制进去,确保已经安装好了下方的模块库。
pip install opencv-python
pip install tensorflow
1. 等待模型加载(加载完成后会弹出摄像头)
2. 按下键盘的A则获取当前摄像头截图加入A训练集
3. 以此类推添加B、C训练集
4. 观察屏幕输出的预测结果
'''
import cv2
import tensorflow as tf
from tensorflow.keras.applications import MobileNet
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.mobilenet import preprocess_input
from tensorflow.keras.models import Model
import numpy as np
# 定义KNN分类器类
class KNNClassifier:
def __init__(self):
# 初始化存储不同类别样本特征的字典,包含A、B、C三个类别
self.examples = {"A": [], "B": [], "C": []}
def add_example(self, activation, class_id):
# 将新的特征向量添加到对应类别的样本集中
self.examples[class_id].append(activation)
def predict_class(self, activation):
# 初始化存储各类别距离的字典
distances = {}
# 遍历所有类别及其对应的样本
for class_id, examples in self.examples.items():
# 计算当前特征向量与该类别所有样本的平均欧氏距离
distances[class_id] = np.mean(
# np.linalg.norm 计算向量范数 范数是衡量向量"大小"或"长度"的数学概念。最常用的是2范数(也叫欧氏范数),就是我们熟悉的向量长度公式。
[np.linalg.norm(act - activation) for act in examples]
)
# 找到距离最小的类别作为预测结果
predicted_class = min(distances, key=distances.get)
# 根据距离计算置信度,距离越小置信度越高
confidence = 1 / (1 + distances[predicted_class])
# 返回预测类别和置信度
return predicted_class, confidence
def main():
# 创建KNN分类器实例
classifier = KNNClassifier()
# 初始化摄像头,参数0表示默认摄像头
webcam = cv2.VideoCapture(0)
print("Loading MobileNet...")
# 加载预训练的MobileNet模型,使用ImageNet权重
base_model = tf.keras.applications.MobileNet(weights="imagenet")
# 创建特征提取模型,输出conv_preds层的特征(用于分类的高级特征)
model = Model(
inputs=base_model.input, outputs=base_model.get_layer("conv_preds").output
)
print("Successfully loaded model")
# 定义三个分类类别
classes = ["A", "B", "C"]
# 主循环,持续处理摄像头画面
while True:
# 从摄像头读取一帧图像
ret, frame = webcam.read()
# 将图像调整为224x224像素,这是MobileNet的输入尺 寸要求
frame = cv2.resize(frame, (224, 224))
# 将OpenCV图像转换为Keras可处理的数组格式
img = image.img_to_array(frame)
# 增加批次维度,从(224,224,3)变为(1,224,224,3)
img = np.expand_dims(img, axis=0)
# 使用MobileNet专用的预处理函数,标准化像素值
img = preprocess_input(img)
# 通过模型提取图像的高级特征向量
activation = model.predict(img)
# 检测键盘输入,等待1毫秒
key = cv2.waitKey(1)
# 如果按下'a'键,将当前特征添加到类别A的训练样本中
if key == ord("a"):
classifier.add_example(activation, "A")
# 如果按下'b'键,将当前特征添加到类别B的训练样本中
elif key == ord("b"):
classifier.add_example(activation, "B")
# 如果按下'c'键,将当前特征添加到类别C的训练样本中
elif key == ord("c"):
classifier.add_example(activation, "C")
# 只有当至少有一个A类别的样本时才进行预测
if len(classifier.examples["A"]) > 0:
# 使用KNN算法预测当前图像的类 别
predicted_class, confidence = classifier.predict_class(activation)
# 打印预测结果和置信度
print(f"Prediction: {predicted_class}, Confidence: {confidence}")
# 显示摄像头画面窗口
cv2.imshow("Webcam", frame)
# 如果按下ESC键(ASCII码27),退出循环
if key == 27: # ESC key to break from the loop
break
# 释放摄像头资源
webcam.release()
# 关闭所有OpenCV窗口
cv2.destroyAllWindows()
# 运行主函数
main()
朴素贝叶斯
这个算法是建立在贝叶斯理论上的分类方法。
它的假设条件是自变量之间相互独立。
简言之,朴素贝叶斯假定某一特征的出现与其它特征无关。即给定类别,特征之间没有相关性。这个假设是“朴素”的来源。
比如说,如果一个水果它是红色的,圆状的,直径大概 7cm 左右,我们可能猜测它为苹果。即使这些特征之间存在一定关系,在朴素贝叶斯算法中我们都认为红色,圆状和直径在判断一个水果是苹果的可能性上是相互独立的。
一个二分类的案例假设:
我今天收到了 100 封邮件,其中有 80 封是垃圾邮件,20 封是正常邮件。
P(垃圾邮件) = 80/100 = 0.8
P(正常邮件) = 20/100 = 0.2
我选定了一些词作为特征,这些词可能出现在邮件中,也可能不出现。这些词有“免费”,“恭喜”,“辛苦”等。
我发现垃圾邮件中有 20 封含有“免费”这个 词,50 封含有“恭喜”这个词,0 封含有“辛苦”这个词。
P(免费|垃圾邮件) = 20/80 = 0.25
P(恭喜|垃圾邮件) = 50/80 = 0.625
P(辛苦|垃圾邮件) = 0/80 = 0
正常邮件中有 5 封含有“免费”这个词。6 封含有“恭喜”这个词,2 封含有“辛苦”这个词。
P(免费|正常邮件) = 5/20 = 0.25
P(恭喜|正常邮件) = 6/20 = 0.3
P(辛苦|正常邮件) = 2/20 = 0.1
现在我收到了一封邮件,这封邮件内容为:“恭喜您获得了一次免费的机会”,我想知道这封邮件是垃圾邮件的概率是多少?
P(垃圾邮件|免费,恭喜) = P(免费|垃圾邮件)* P(恭喜|垃圾邮件)* P(垃圾邮件)= 0.25 * 0.625 * 0.8 = 0.125
P(正常邮件|免费,恭喜) = P(免费|正常邮件)* P(恭喜|正常邮件)* P(正常邮件)= 0.25 * 0.3 * 0.2 = 0.015
因为 P(垃圾邮件|免费,恭喜) > P(正常邮件|免费,恭喜),所以这封邮件被判定为垃圾邮件。
如果狡猾的垃圾邮件制造者把邮件内容改为:“恭喜您获得了一次免费的机会,辛苦