Python - AI课程笔记

上一篇:Python环境:Anaconda、PyTorch、YOLO

测试数学表达式

结果:很遗憾,我的博客暂时不支持。以后再说吧,或者 不搞了,不太重要。

$ x = \frac{x - \mu}{\sigma}$
123
$x = \frac{x - \mu}{\sigma}$

$ x = \frac{x - \mu}{\sigma}$

123

$x = \frac{x - \mu}{\sigma}$

1
2
3
4
5
$ x = \frac{x - \mu}{\sigma}$

123

$x = \frac{x - \mu}{\sigma}$

开干

2024-11-19 08:14:32

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
大模型
难易程度:低
不需要数学 - 废物都能学会
深度学习
难易程度:中
数学依赖少
神经网络:每个流程都是八股文 - 傻子都能学得会
pytorch框架 取代 tensorflow
机器学习
难易程度:高
数学依赖高 - 不会数学也会学的很好
算法本身用的少,但是算法的思想用的地方多
预留2周时间搞定,学习其思想很重要

大模型是重点,人工智能是必须。

计算机视觉
图像 -> 图像
视频(抽帧)-> 图像
卷积 - 傅里叶变换
目标检测:位置、分类
图像分割:抠图
YOLO 取代 CV
- 把市面上所有好的视觉算法统一起来
- 任务处理3行代码,傻子都能用

深度学习 - 大众熟知 - 因为CV
之前 CV=70% NLP=30%
今天 大模型=70% CV=10% 其他=20%

公司做技术活不下去,公司还要是产品和营销。

三大方向
1.文本分类 2.序列标注 3.文本生成
NLP(自然语言处理) - 换数据,不换模型
CV - 每个分支都有自己的方向和算法

RNN
- 循环神经网络(Recurrent Neural Network)
- 时序循环,前后依赖(后词需要前词,第100词 需要 第99词---词多前面会忘记)

Transformer - 引爆大模型时代 AIGC
- 可以并行,投钱就可以(拼硬件)
- 百万上下文都可以,不丢信息
- 数据量越多,效果越好

纯技术来说:大模型 = NLP高级阶段

AIGC
冲突
(我创作,你分析)
CV - 给图像,做图像分析
NLP - 给文本,做文本分析

(我提示,你创作)
AIGC - 给你几个关键词,给我画个图,生成一段文稿,

创作的东西,只有人才能做。
莫言 - 3k字 一本小说,诺贝尔文学奖

AIGC大模型
- 给几个想法,比学了多年美术的人,画的还好
- 写作也是

鸟会飞,鱼会游,但没有我聪明啊。
中国人 学英语,学了多年,狗屁不是。 --- 大模型 - 翻译的很精准。
学了多年的天文地理,你一问大模型,无所不能。

一个人搞大模型,年薪百万,千万。

大模型训练
- 数据:3000w本红楼梦
- 千卡平台(贵族专用)

大模型落地
底层
- LLM + 向量
- GPU
- 微调
- API(应用层)
应用
langchain框架(大模型能力中台)
- prompt
- RAG - 检索增强生成(retrieval-augmented generation)
- agent - 智能体开发
- API(Restful API - 业务系统)
业务系统
- RPA - 机器人/数字人
- CRM - 客户管理系统
- ERP - 企业资源管理系统
- 小程序

传统的 前端、后端 都有,可以调用大模型的 API
底层数据库,应用Java --- 现在被大模型解脱了一些能力

一个人搞定所有

老师
- 西门子,小马仔,维护设备
- 华润,核磁图像生成,信号处理,图像处理
- 合伙创业,合伙人政治,动不动就千万,
- 自己创业
- 十多年前,读硕士,---之前都是说人工智能是骗子

搞技术 = 古代将军,战乱在打,和平时代,朱元璋都杀了。
只搞技术,半吊子。
抬头看路,技术搞定,业务也要搞定。(业务-技术,双修)

人工智能领域,智力游戏,不是体力活 - 不比代码量,比应用。
业务的痛点是什么,用技术解决。
RAG系统 - 10行代码
Agent开发 - 5行代码
YOLO应用 - 3行代码

【闭环】
你需要强的业务洞察能力,可以用什么技术来解决 - 这是最重要的。
还可以看得懂技术,哪个技术更好,能解决什么问题。
又能把这个技术给实现了,你还能形成完整的方案,还能够给别人讲清楚这个方案,
还能够把这个方案卖出去,还能够把钱收回来,还让人家觉得受益匪浅。

【商业能力】
业务痛点,什么技术解决,怎么解决、实现,
形成方案,促成对方付费

【专题】
语音识别 - 别人做厉害了(科大讯飞-极致),你要想怎么应用
人脸识别 - 别人做厉害了(刷脸进火车站,付款),你要想怎么应用
自动驾驶
医疗AI

心理
- 不要说。自己数学不好,
- 最好是搞市场的,推导公式-学生,大学老师没在市场呆过,好老师-市场/学校都呆过,
带着业务的理解、痛点,去搞技术。
变不了现的技术,垃圾,只能打工。
明白了有输出的输入,输入就快了。

不要买书,买了 看不懂/不看。
不要买电脑。 大模型前,不需要配置。 大模型 - 配置云服务器。
数学基础差怎么办?没有这一条,51期,前面50期,学得好的都是又傻又笨的,学的不好的都是聪明的。
- 学的不好的,每天说自己这个不如人家,那个不如人家,每天很累很辛苦。
- 学的不好的,今天找这个视频看看,书看看,搞了好多书,视频。也很辛苦,一事无成。
- 学的好的,我说什么,就做什么。(书看几遍,代码优化一下,无效社交全干掉,)
直播要跟,3天不跟,后面就赶不上了。(往死里跟)
计算机高手,没有看书成长的,都是工程蹂躏出来的。

混的不好,所有社交都没用。
混的好了,所有社交都没用,自己就来了。

D1-人工智能、KNN算法

2024-11-20 07:42:16

Python编程-jupyter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
- 了解基本的 `Python` 语法即可!
- 编程能力的提升跟算法的学习是同步的!
- Python编程基本操作:
- code 模式:
- 执行 `cmd` 命令:! + 命令
- ! pip install numpy
- 执行 `Python` 代码
- Markdown 模式:
- Markdown 标记
- Latex 公式

- * cell 单元格的两种模式:
- 命令模式:ESC
- 选中,但是不能输入内容,可以输入命令
- A:在上面添加一个单元格
- B:在下面添加一个单元格
- M:切换到 Markdown 模式
- Y:切换到 code 模式
- DD:删除当前单元格
- 编辑模式:Enter
- 选中,同时可以输入内容,不等你输入命令
- Ctrl + Enter:执行当前单元格
- Shift + Enter:执行当前单元格,并进入下一行
- Tab:代码补全
- Ctrl + /:注释
- Shift + Tab:查看函数的参数

- Python 三板斧
- Python 的学习都是现学现卖!
- 1、print 查看内容
- 2、type 查看类型
- 3、dir 查看功能

- Running - showdown关闭运行

- 在线环境:
- modelscope.cn
- 注册账号,关联阿里云,申请免费资源
- 作为备用即可

机器学习-简介

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
- * 算法:偏抽象的概念,是计算机解决问题的步骤和流程!
- * 模型:偏具体的概念,用代码去实现某一个算法!
- 数学本质:
- $ y = f(x) $
- X:是样本的特征
- y: 是样本的标签
- 把 X 映射 为 y
- * 机器学习的项目流程:
- Step1: 项目分析:
- 先关注外部特性:
- 输入 x 是什么?
- 输出 y 是什么?
- 分类项目?
- 回归项目?
- Step2: 数据采集:
- 根据输入和输出,构建数据集
- 本质上:数理统计问题
- 采集总体的一个样本集,通过样本集的统计量来估计总体的统计量
- 分层采样
- 结构化数据:成行成列
- 每行一个样本:
- 独立同分布
- 每列一个特征:
- 独立的
- 离散型变量:
- 不同的状态值
- 高/低
- 连续型变量
- 长度,深度等程度问题
- 长度,10.5米
- Step3: 数据的预处理:
- 数据清洗:
- 重复值
- 缺失值
- 异常值
- 无效特征

- 数据切分:
- 训练集:训练过程中,用来训练模型(模型的学习数据)
- 验证集:在训练过程中,用来验证模型的效果(不参与学习过程)
- 测试集:训练完成后,用来评估模型的效果(不参与学习过程)
- 在实际工作中,经常把验证集和测试集合并,一起使用

- 预处理:
- 中心化
- 归一化
- 标准化

- Step4: 选择一个模型,完成 X 到 y 的映射:
- 分类问题:预测的标签是一个离散型的变量!
- 逻辑回归
- KNN
- 朴素贝叶斯
- 支持向量机
- 决策树
- 集成学习
- 回归问题:预测的是一个连续型变量!
- 线性回归
- KNN
- 支持向量机
- 决策树
- 集成学习

- Step5: 训练模型
- 把训练集的特征 `X_train` 和标签 `y_train` 给模型 `fit` 方法,进行训练
- 本质:
- 学习的过程!
- 学习如何把 `X` 映射为 `y`

- Step6: 评估模型
- 分类问题:
- 准确率:accuracy
- 召回率:recall
- 精准率:precision
- F1-score
- 回归问题:
- MAE:平均绝对误差
- MSE:平均平方误差

- Step7: 保存和部署模型
- 保存:把模型保存到文件中
- pickle
- joblib
- dump
- 部署:反序列化模型
- pickle
- joblib
- load

代码实践-KNN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
--------------------------------
鸢尾花识别
- iris
- 项目需求:鸢尾花有3个子品种,想通过机器学习算法来做分类预测!
- 进一步思考:
- 任务:给定一朵花,让模型识别到底是哪个子品种!
- 输入:一朵花
- 一朵花是不能直接输入计算机中
- 特征工程:
- 数字化转型
- 抽取/构建跟这朵花的类别有关系的特征来代表这朵花!!!
- 跟业务专家详细咨询:
- 花萼长度 ===> 特征1 = x1
- 花萼宽度 ===> 特征2 = x2
- 花瓣长度 ===> 特征3 = x3===>
- 花瓣宽度 ===> 特征4 = x4
- 输出:子品种
- 分类问题:
- 对状态进行编码:
- N个状态:
- 0, ..., N-1
- 3个类别:
- 0
- 1
- 2
--------------------------------
  1. 加载数据
    1
    2
    3
    4
    5
    6
    7
    from sklearn.datasets import load_iris

    # 获取数据
    X, y = load_iris(return_X_y=True)

    # X.shape # (150, 4) [sample_size, num_features]
    # y.shape # (150,) sample_labels
  2. 切分数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    from sklearn.model_selection import train_test_split

    # 20% 测试集 80% 训练集
    # 训练数据 X_train y_train
    # 测试数据 X_test y_test
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
    # X_train.shape (120, 4)
    # y_train.shape (120,)
    # X_test.shape (30, 4)
    # y_test.shape (30,)
  3. 套用模型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from sklearn.neighbors import KNeighborsClassifier

    # 实例化对象
    knn = KNeighborsClassifier()

    # 训练模型
    knn.fit(X=X_train, y=y_train)

    # 模型预测
    y_pred = knn.predict(X=X_test)

    # y_pred # 预测结果 array([2, 1, 0, 2, 0, 2, 0, 1, 1, 1, 2, 1, 1, 1, 2, 0, 1, 1, 0, 0, 2, 1, 0, 0, 2, 0, 0, 1, 1, 0])
    # y_test # 真实结果 array([2, 1, 0, 2, 0, 2, 0, 1, 1, 1, 2, 1, 1, 1, 1, 0, 1, 1, 0, 0, 2, 1, 0, 0, 2, 0, 0, 1, 1, 0])
  4. 模型评估
    1
    2
    3
    4
    5
    # y_pred == y_test   # array([ True,  True,  True,  True,  True,  True,  True,  True,  True, True,  True,  True,  True,  True, False,  True,  True,  True, True,  True,  True,  True,  True,  True,  True,  True,  True, True,  True,  True])

    # acc(准确率 accuracy rate)
    # mean 求平均
    acc = (y_pred == y_test).mean() # 0.9666666666666667
  5. 模型的保存和加载
    1
    2
    3
    4
    5
    6
    7
    import joblib

    # 模型的保存
    joblib.dump(value=knn, filename="knn.model") # 当前文件同级目录生成 knn.model 模型文件

    # 模型的加载
    model = joblib.load(filename="knn.model")

D2-科学计算、手写KNN

2024-11-21 07:26:30

传统编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# list
scores = [11, 22, 33]

# 数个数
len(scores) # 3

# 平均分
sum(scores) / len(scores) # 22.0

# 最高分
max(scores) # 33

# 最低分
min(scores) # 11

# 方差???
# 每个同学加5分???

科学计算-numpy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
"""
- 向量化计算
- 矩阵计算
- 高性能计算
- 线性代数
- GPU之类的
- NumPy(底层C++):
- 科学计算神器!
- 通用科学计算库
- 深度学习框架 PyTorch,本质上是一个 NumPy++
- 容器:NDArray N为数组
- 算法:各种数据处理方法
"""
import numpy as np

scores = [11, 22, 33]
arr = np.array(scores)

arr # array([11, 22, 33])

# 5形状展开 等同于 [11, 22, 33] + [5, 5, 5]
arr + 5

arr.min() # 11
arr.max() # 33
arr.size # 3

# 平均
arr.mean() # 22.0

# 方差
arr.var() # 80.66666666666667

# 标准差
arr.std() # 8.981462390204987

# 连续均匀分布 [0, 1)
# np.random.rand()
# 离散均匀分布 [low, high)
# np.random.randint(low=0, high=101, size=30)
# 标准正态分布, mu=0, sigma=1,
# ===> 也叫:标准高斯分布
# np.random.randn(10)
a = np.random.randint(low=0, high=6, size=3) # array([1, 1, 3])
b = np.random.randint(low=0, high=6, size=3) # array([2, 0, 4])

# 向量的模
(a ** 2).sum() ** 0.5 # 3.3166247903554
np.linalg.norm(a) # 3.3166247903554

# 向量求点乘积 也叫内积 也叫点积(dot product)
(a * b).sum() # 14
a @ b # 14

余弦相相似度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
"""
- 本质:使用向量的 `夹角` 余弦来代表向量的相似情况
- 0°:相似度为1,最高的
- 90°:相似度为0,中等
- 180°:相似度为-1,反向
- 余弦相似度越大代表两个向量越相似
- * 的确存在逻辑上的错误!!!(相亲只看核心问题“高富帅”,其他不考虑)
- 大数据的核心是:工程折中!!!不是精准核算!!!(向量之间,只考虑夹角,不考虑长度)
- 从纯数学的角度来看,一切模型都是错的!!
- 从工程的角度来看,大部分模型是有效的!!
- 半科研半工程(实用性)
--------------------------------
- 看待数据两个视角:
- 线性代数(向量)
- 余弦相似度
- 余弦相似度越高,代表样本越相似
- 欧式空间(点)
- 欧式距离(平面直角坐标系,两点坐标之间距离)
- 欧式距离越小,代表样本越相似
"""

# 余弦相似度
cosine_similarity = a @ b / np.linalg.norm(a) / np.linalg.norm(b)
cosine_similarity # 0.9438798074485388
a # array([1, 1, 3])
b # array([2, 0, 4])

# 欧式空间的角度
# ===> 二维理解,两个点之间的欧式距离 ((x2 - x1) ** 2) + (y2 - y1) ** 2) ** 0.5
((a - b) ** 2).sum() ** 0.5 # 1.7320508075688772
np.linalg.norm(a - b) # 1.7320508075688772

KNN-再回顾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
"""
sklearn 所有算法 => 引入,创建,训练,预测,评估,
"""

# 1. 加载
from sklearn.datasets import load_iris
X, y = load_iris(return_X_y=True)

# 2. 切分
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

# 3. 训练
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X=X_train, y=y_train)

# 4. 预测
y_pred = knn.predict(X=X_test)
y_pred
y_test

# 5. 评估
acc = (y_pred == y_test).mean()
acc # 0.9666666666666667

Counter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from collections import Counter

labels = [1, 2, 3, 3, 3, 3, 3, 3, 2, 2, 1, 1, 1]

Counter(labels) # Counter({3: 6, 1: 4, 2: 3})

Counter(labels).most_common() # [(3, 6), (1, 4), (2, 3)] ()

Counter(labels).most_common(0) # []

Counter(labels).most_common(1) # [(3, 6)]

Counter(labels).most_common(2) # [(3, 6), (1, 4)]

Counter(labels).most_common(1)[0] # (3, 6)

Counter(labels).most_common(1)[0][0] # 3 --- 3出现最多

手写KNN-分类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
"""
- K Nearest Neighbors K个最近的邻居
- 核心思想:
- 近朱者赤,近墨者黑
- 如何做一个样本的推理:
- 分类问题:x0到底属于哪一类呢?
- 寻找 x0 的 K 个最近的邻居
- 统计这 K 个邻居中哪个类别出现的最多
- 这个出现次数最多的类别,就是 x0 的类别
- 回归算法:x0到底是多少?
- 寻找 x0 的 K 个最近的邻居
- 统计这 K 个邻居标签的均值
- 这个标签的均值就是 x0 的值

- 算法特点:
- 惰性计算(几乎没有训练过程,在推理时直接硬计算,这不属于典型的人工智能!)
- 如何实现?
- 全面模仿 sklearn
"""

from collections import Counter
import numpy as np


class MyKNeighborsClassifier(object):
"""
自定义KNN分类算法
"""

def __init__(self, n_neighbors=5):
"""
初始化方法:
- 接收 超参数
"""
self.n_neighbors = n_neighbors

def fit(self, X, y):
"""
训练过程
"""
self.X = X
self.y = y

def predict(self, X):
"""
推理过程
"""
# X:[batch_size, num_features]

# 第一步:寻找样本的 K个邻居
# 第二步:对 K 个邻居的标签进行投票
results = []
for x in X:
distance = ((self.X - x) ** 2).sum(axis=1) ** 0.5 # 距离
idxes = distance.argsort()[:self.n_neighbors] # 5个最相似下标
labels = self.y[idxes] # 5个相似元素
final_label = Counter(labels).most_common(1)[0][0] # 拿出5个元素中,最相似的那个
results.append(final_label)
return np.array(results)


my_knn = MyKNeighborsClassifier(n_neighbors=5)
my_knn.fit(X=X_train, y=y_train)
y_pred = my_knn.predict(X=X_test)
(y_pred == y_test).mean() # 0.9666666666666667(结果一致)

手写KNN-回归-房价预测

预测的是一个连续数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import pandas as pd

# 导入数据《波士顿房价数据》
data = pd.read_csv(filepath_or_buffer="boston_house_prices.csv", skiprows=1)
data.shape # (506, 14) 506行14列

X = data.drop(columns=["MEDV"]).to_numpy()
y = data["MEDV"].to_numpy()
X.shape # (506, 13) 所有列,除了“MEDV”列
y.shape # (506,) 只取“MEDV”列

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

from sklearn.neighbors import KNeighborsRegressor

knn = KNeighborsRegressor(n_neighbors=5)
knn.fit(X=X_train, y=y_train)
y_pred = knn.predict(X=X_test)

# MAE --- 平均绝对误差(Mean Absolute Error)
abs(y_pred - y_test).mean() # 4.756078431372549

# MSE --- 均方误差(mean square error)
((y_pred - y_test) ** 2).mean() # 51.74387450980392


# ------------------------------------------------------
# -------------------手动实现----------------------------
# ------------------------------------------------------

class MyKNeighborsRegressor(object):
"""
自定义KNN回归算法
"""

def __init__(self, n_neighbors=5):
"""
初始化方法:
- 接收 超参数
"""
self.n_neighbors = n_neighbors

def fit(self, X, y):
"""
训练过程
"""
self.X = X
self.y = y

def predict(self, X):
"""
推理过程
"""
# X:[batch_size, num_features]

# 第一步:寻找样本的 K个邻居
# 第二步:对K个邻居的标签取均值
results = []
for x in X:
distance = ((self.X - x) ** 2).sum(axis=1) ** 0.5
idxes = distance.argsort()[:self.n_neighbors]
labels = self.y[idxes]
final_label = labels.mean() # 5个值的均值(之前写的是求出最相似的那个)
results.append(final_label)
return np.array(results)


knn = MyKNeighborsRegressor(n_neighbors=5)
knn.fit(X=X_train, y=y_train)
y_pred = knn.predict(X=X_test)
abs(y_pred - y_test).mean() # 4.756078431372549(结果一致)
((y_pred - y_test) ** 2).mean() # 51.74387450980392(结果一致)

day03-概率论、决策树

2024-11-22 08:05:51

概率论

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
"""
1. 数学课
- 高等数学
- 模型优化
- 概率论和数理统计
- 建模思想
- 线性代数
- 高性能计算

*总结
高等数学 - 点 坐标 函数图像,
概率 统计 - 彩票中奖
线性代数 - 完全不知道干嘛的(但人工智能主要靠它)
--------------------------------
2. 人工智能项目的本质
- 统计学项目
- 样本分析 -> 评估总体
--------------------------------
3. 概率
- probability
- 或然率,概率,频率,可能行,置信度,打分 ...
- 离散型变量:
- 有限个状态
- 状态之间无大小、程度等概念
- 状态之间是严格对立的!!!
- *概率的计算:
- 转换为频率
- 数个数
- 连续型变量
- 无限个数值
- 数值之间是大小、程度的差异
- 内涵是一致的
- 概率的计算:
- Probability Density Function
- 通过对 **概率密度函数** 的积分来求解
- **概率密度函数** 是 **概率** 的导数
- **概率** 就是 **概率密度函数** 的积分
- *计算步骤(理论数学):
- 先找出概率密度函数来
- 然后再对概率密度函数积分
- 在任何一点的概率都是零
===>(单点无法求积分,所以=0。但最高点比最低点明显有差值,因此理论数学不合适)
- *计算步骤(工程数学):
- 把连续型变量的分布看作是高斯分布(不是高斯的话,只是结果差了点)
- 直接拿概率密度函数的值代替概率

朴素贝叶斯(条件 -> 概率)
--------------------------------
"""

import numpy as np
from matplotlib import pyplot as plt

x = np.linspace(start=-5, stop=5, num=100)


# 正态分布
def normal(x, mu=0, sigma=1):
return 1 / (2 * np.pi) ** 0.5 / sigma * np.exp(-(x - mu) ** 2 / 2 / sigma ** 2)


# 折线图
plt.plot(x, normal(x, mu=0, sigma=1), label="$\mu=0,\sigma =1$")
plt.plot(x, normal(x, mu=1, sigma=1), label="$\mu=1,\sigma =1$")
plt.plot(x, normal(x, mu=0, sigma=2), label="$\mu=0,\sigma =2$")
plt.grid() # 网格
plt.legend() # 显示图例

from sklearn.datasets import load_iris

X, y = load_iris(return_X_y=True)

x0 = X[:, 0]
mu = x0.mean() # 5.843333333333334
sigma = x0.std() # 0.8253012917851409

normal(x=6.3, mu=mu, sigma=sigma) # 0.41477435368825577

决策树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
"""
- Decision Tree
- 自身就是很好的算法!!!
- 解释性比较强!!!
- 还能对特征重要性排序!!!
- 它可大可小!!!
- 它是集成学习的基础!!!
- CART:分类和回归树
- 训练:
- 构建一棵决策树
- 推理:
- 利用决策条件来做推理!
"""

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import plot_tree
from sklearn.tree import DecisionTreeClassifier

X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
dtc = DecisionTreeClassifier(max_depth=2, criterion="entropy")
dtc.fit(X=X_train, y=y_train)

# 特征重要性
dtc.feature_importances_ # array([0. , 0. , 0.66489397, 0.33510603])

plot_tree(dtc)

X_train.shape # (120, 4)

# 通过枚举遍历寻找最优分裂点
for feature_idx in range(4):
print(feature_idx)
for feature_value in set(X_train[:, feature_idx]):
y_left = y_train[X_train[:, 0] <= feature_value]
y_right = y_train[X_train[:, 0] > feature_value]
print(y_left, y_right)
print("-" * 100)
break

熵 entropy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
"""
- 香农
- 混乱程度
- 不纯度 impurity
- 不确定性,拿不准
- 拿得准:输入a类花,获取到a类。输入b类花,获取到b类。
- 拿不准:输入a类花,不一定获取到a类。(训练模型不准)
- 系统越混乱,熵就越大!!!
- 对于分类问题:
- 类别越多,每个类别的概率越均等,熵越大!
- 信息熵
- 基尼系数 gini(熵的简化)
- 对于回归问题:
- 数据越集中,熵越小,数据越分散,熵越大!
- 方差(均方误差 mse = mean square error)
- 香农观点:
- 一个模型,在没有训练时,混乱程度是最高的!
- 随着训练的进行,混乱程度逐渐降低!
- 所以,模型训练的过程,就是系统熵在不断下降的过程!
- 算法好,熵下降的很快!!
"""

import numpy as np

p1 = np.array([0.5, 0.5])
entropy_p1 = 0.5 * np.log2(1 / 0.5) + 0.5 * np.log2(1 / 0.5)
entropy_p1 # 1.0

p2 = np.array([0.9, 0.1])
entropy_p2 = 0.9 * np.log2(1 / 0.9) + 0.1 * np.log2(1 / 0.1)
entropy_p2 # 0.46899559358928133

num = np.array([39, 37, 44])
p = num / num.sum() # 概率 array([0.325 , 0.30833333, 0.36666667])
(p * np.log2(1 / p)).sum() # 熵 1.5810951599090193

day04上-数据读取、模型预测、FastAPI

2024-11-25 08:02:29
整理 2024-11-26

问题引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
1. 如何预测客户是否流失?
- 拉新
- 留存
- 流失 VS 留存?
- 参考用户的一些历史(业务)行为
- 人口统计学信息(user 表中)
- 年龄
- 性别
- 学历
- 婚姻
- ...
- 业务信息
- 购买记录
- 浏览记录
- ...
- 先去猥琐发育一段时间,搞一些原始的累积
- 历史数据:大量留存的用户 + 大量流失的用户
- 预测:二分类即可
- 根据历史数据,做二分类预测

--------------------------------

2. 数据存在的一些疑惑?
- 无法判定哪些特征有用,哪些特征没用!
- 都先留着!
- 离散型变量,都是非数字的标准格式
- 状态编码
- zero index
- 0 ~ N-1
- 例如:
'apple' -> 0
'banana' -> 1
'cherry' -> 2
- 不会增加特征的维度
- 但是会潜在引入状态大小问题
- 本质上:把离散变量当做连续变量处理了!!!
- one hot
- state 0: [1, 0, 0, 0, ..., 0]
- state 1: [0, 1, 0, 0, ..., 0]
- state N: [0, 0, 0, 0, ..., 1]
- 例如:
'apple' -> [1, 0, 0]
'banana' -> [0, 1, 0]
'cherry' -> [0, 0, 1]
- 一个特征变成N个特征
- 构建了一个稀疏矩阵(大量的0,少数的有用数据)
- 浪费了存储和计算
- 特征之间距离相等,互相垂直

总结:
目的不同:零索引主要用于对元素进行定位或计数,而一位有效编码则是为了将分类数据转换成机器学习算法可以处理的形式。
表现形式不同:零索引是一个简单的整数,而一位有效编码则是一个向量。
适用场景不同:当需要简单地标识或访问数组中的元素时,通常使用零索引;而在处理分类数据,特别是准备数据以供机器学习模型使用时,会使用一位有效编码。

- 连续型变量:
- 数量级不同,量纲不同
--------------------------------

数据读取-pandas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
"""
1. 数据读取
"""
import pandas as pd
data = pd.read_csv(filepath_or_buffer="./信用卡客户流失数据集.csv")
data.shape # (10134, 21)
data.info() # 查看数据格式,文件大小

data.head() # 查看前5行
data.head(n=5)

data.tail(n=3) # 查看后3行
data.sample(n=3) # 随机取3行


"""
2. 数据清洗
- 重复
- 什么叫重复?user_id?
- 缺失
- 异常
"""
# 只是判断是否有重复
data.duplicated(subset=["CLIENTNUM"]).sum() # 5 --- 重复了5条

# 删除重复
data.drop_duplicates(subset=["CLIENTNUM"], inplace=True)
data.info() # (10129, 21)

# 判断是否缺失
data.isna().sum()

# 判断是否缺失
data.isna().sum()
# 删除缺失数据
data.dropna(how="any", inplace=True)
data.info() # (10127, 21)
data.sample(n=3) # 随机取3条看看

# 删除无用的列
data.drop(columns=["CLIENTNUM"], inplace=True)
data.info() # (10127, 20)


"""
3. 数据预处理
- 编码问题
- 异常问题
"""
data.shape # (10127, 20)

# 1, Attrition_Flag
#
# 判断有多少个离散的状态
# array(['Existing Customer', 'Attrited Customer'], dtype=object)
data["Attrition_Flag"].unique()
# 构建 状态字典
# {'Existing Customer': 0, 'Attrited Customer': 1}
attrition_dict = {attrition: idx for idx, attrition in enumerate(data["Attrition_Flag"].unique())}
# 更新对应的列
data["Attrition_Flag"] = data["Attrition_Flag"].apply(func=lambda ele: attrition_dict[ele])
# 此列数据 -> 下标
# array([0, 1], dtype=int64)
data["Attrition_Flag"].unique()
# 下标转标签
# {0: 'Existing Customer', 1: 'Attrited Customer'}
idx2label = {idx: attrition for attrition, idx in attrition_dict.items()}

# 2, Customer_Age
#
# 异常判断
# 0 --- 无异常数据
((data["Customer_Age"] < 0) | (data["Customer_Age"] > 150)).sum()

# 3, Gender
# 判断有多少个离散的状态 array(['M', 'F'], dtype=object)
data["Gender"].unique()
# 构建 状态字典 {'M': 0, 'F': 1}
gender_dict = {gender: idx for idx, gender in enumerate(data["Gender"].unique())}
# 更新对应的列 Gender洗数据 - 0 或 1
data["Gender"] = data["Gender"].apply(func=lambda ele: gender_dict[ele])

# 4, Dependent_count array([3, 5, 4, 2, 0, 1], dtype=int64)
data["Dependent_count"].unique()

# 5, Education_Level
data["Education_Level"].unique()
# 构建 状态字典 {'High School': 0, 'Graduate': 1, 'Uneducated': 2, 'Unknown': 3, 'College': 4, 'Post-Graduate': 5, 'Doctorate': 6}
education_dict = {education: idx for idx, education in enumerate(data["Education_Level"].unique())}
# 更新对应的列
data["Education_Level"] = data["Education_Level"].apply(func=lambda ele: education_dict[ele])

# ... 数值列都不用清洗, 字符串洗成下标 0 ~ N-1
# 清洗完,查看一下数据
data.sample(n=3)


"""
4. 数据拆分
"""
data.info() # (10127, 20)
y = data["Attrition_Flag"].to_numpy() # (10127,) 10127行 1列
X = data.drop(columns=["Attrition_Flag"]).to_numpy() # (10127, 19) 10127行 19列
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)


"""
5. 数据标准化处理
χ=(χ-μ)/σ

$x = \frac{x - \mu}{\sigma}$
"""
# 从训练集中抽取预处理参数 mu 和 sigma
mu = X_train.mean(axis=0)
sigma = X_train.std(axis=0)

# 执行标准化操作
X_train = (X_train - mu) / sigma
X_test = (X_test - mu) / sigma


"""
6. 数据保存
- 特征编码时的状态字典
- 特征的各种异常判断
- 标准化处理的参数
"""
import joblib
state_dict = [idx2label, gender_dict, education_dict, marital_dict, income_dict, card_dict, mu, sigma]
data = [X_train, y_train, X_test, y_test]
joblib.dump(value=[state_dict, data], filename="all_data.lxh")

建模预测-算法尝试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
"""
1. 数据加载
"""
# 加载数据,结构
import joblib
[state_dict, data] = joblib.load(filename="all_data.lxh")
idx2label, gender_dict, education_dict, marital_dict, income_dict, card_dict, mu, sigma = state_dict
X_train, y_train, X_test, y_test = data

# 备份 state_dict
joblib.dump(value=[idx2label, gender_dict, education_dict, marital_dict, income_dict, card_dict, mu, sigma], filename="state.lxh")


"""
2. 尝试所有算法
- 二分类问题
- 算法(下面逐步实现):
- KNN
- 朴素贝叶斯
- 决策树
- 评价指标
- 准确率 accuracy
- 测试结论:决策树 > KNN > 朴素贝叶斯
"""


"""
2.1 KNN算法
"""
from sklearn.neighbors import KNeighborsClassifier
# 初始化相关参数
best_n_neighbor = 0
best_acc = 0
best_model = None
# 简单遍历,寻找比较好的 N
# 找到一个更好的模型: 3, 0.9002961500493584
# 找到一个更好的模型: 5, 0.9042448173741362
# 找到一个更好的模型: 7, 0.9067127344521224
# 找到一个更好的模型: 9, 0.9081934846989141
for n_neighbor in range(3, 31, 1):
knn = KNeighborsClassifier(n_neighbors=n_neighbor)
knn.fit(X=X_train, y=y_train)
y_pred = knn.predict(X=X_test)
acc = (y_pred == y_test).mean()
if acc > best_acc:
best_n_neighbor = n_neighbor
best_acc = acc
best_model = knn
print(f"找到一个更好的模型: {best_n_neighbor}, {best_acc}")
# 保存模型
joblib.dump(value=[best_n_neighbor, best_acc, best_model], filename="best_knn.lxh")


"""
2.2 朴素贝叶斯
- 高斯朴素贝叶斯
- 高斯:假定所有的特征都是连续型的
"""
from sklearn.naive_bayes import GaussianNB
gnb = GaussianNB()
gnb.fit(X=X_train, y=y_train)
y_pred = gnb.predict(X=X_test)
acc = (y_pred == y_test).mean()
acc # 0.8820335636722606


"""
2.3 决策树
"""
from sklearn.tree import DecisionTreeClassifier
dtc = DecisionTreeClassifier()
dtc.fit(X=X_train, y=y_train)
y_pred = dtc.predict(X=X_test)
acc = (y_pred == y_test).mean()
acc # 0.932872655478776
# 保存模型
joblib.dump(value=dtc, filename="dtc.lxh")


"""
3. 推理过程
"""
x = "787584108,55,M,3,Unknown,Married,$80K - $120K,Blue,47,4,2,3,3436,2016,1420,0.901,1097,33,0.833,0.587"
x1 = "719808558,55,F,2,Graduate,Married,Less than $40K,Blue,43,2,4,3,1438.3,0,1438.3,0.707,886,27,0.421,0"
def predict(x):
"""
推理函数
"""
import numpy as np
print(f"原始输入:{x}")

x = x.split(",")[1:]
print(f"切分之后:{x}")

temp = []
# 1, age
temp.append(float(x[0]))
# 2, gender
temp.append(gender_dict[x[1]])
# 3 - 7
temp.append(float(x[2]))
temp.append(education_dict[x[3]])
temp.append(marital_dict[x[4]])
temp.append(income_dict[x[5]])
temp.append(card_dict[x[6]])
# 8 - 19
temp.extend([float(ele) for ele in x[7: ]])
print(f"编码之后:{temp}")

# 标准化之后
x = np.array(temp)
x = (x - mu) / sigma
print(f"标准化之后:{x}")

# 模型推理(决策树)
y_pred = dtc.predict(X=[x])
print(f"推理结果:{y_pred}")

# 结果解析
final_result = idx2label[y_pred[0]]
print(f"最终结果:{final_result}")
return final_result

predict(x=x1)
# 原始输入:719808558,55,F,2,Graduate,Married,Less than $40K,Blue,43,2,4,3,1438.3,0,1438.3,0.707,886,27,0.421,0
# 切分之后:['55', 'F', '2', 'Graduate', 'Married', 'Less than $40K', 'Blue', '43', '2', '4', '3', '1438.3', '0', '1438.3', '0.707', '886', '27', '0.421', '0']
# 编码之后:[55.0, 1, 2.0, 1, 0, 1, 0, 43.0, 2.0, 4.0, 3.0, 1438.3, 0.0, 1438.3, 0.707, 886.0, 27.0, 0.421, 0.0]
# 标准化之后:[ 1.07985708 0.94931391 -0.27066045 -0.58408772 -0.85930911 -0.66288823 -0.26234368 0.88414981 -1.16445715 1.62936497 0.48915608 -0.79689049 -1.41786297 -0.66942588 -0.23967428 -1.03061326 -1.6083197 -1.21393127 -0.9884621 ]
# 推理结果:[1]#
# 最终结果:Attrited Customer

FastAPI 快速入门

Github https://github.com/fastapi/fastapi
官方网站 https://fastapi.tiangolo.com/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
"""
服务端 C/S 架构部署
- FastAPI web
"""

"""
- 安装
- pip install "fastapi[standard]"
- [standard] 标记告诉 pip 不仅要安装 FastAPI 本身,还要安装一些额外的包
- 创建文件 "fastapi测试.py",内容如下
- 启动,执行命令 fastapi dev fastapi测试.py
- 测试
- http://127.0.0.1:8000/ 返回 {"Hello":"World"}
- http://127.0.0.1:8000/items/111?q=taopanfeng 返回 {"item_id":111,"q":"taopanfeng"}
"""
from typing import Union
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
return {"Hello": "World"}

@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}

FastAPI C/S案例

1、创建main.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
from fastapi import FastAPI
from pydantic import BaseModel
import joblib

# 加载状态字典
(
idx2label,
gender_dict,
education_dict,
marital_dict,
income_dict,
card_dict,
mu,
sigma,
) = joblib.load("state.lxh")

# 加载模型
model = joblib.load("dtc.lxh")

# 创建一个服务
app = FastAPI()

# 自定义一个参数接受类
class Param(BaseModel):
x: str

class ResultEntity(BaseModel):
result: str

# 创建一个路由
@app.post("/predict")
def read_item(param: Param):
"""
推理函数
"""
x = param.x
import numpy as np
print(f"原始输入:{x}")

x = x.split(",")[1:]
print(f"切分之后:{x}")

temp = []
# 1, age
temp.append(float(x[0]))
# 2, gender
temp.append(gender_dict[x[1]])
# 3 - 7
temp.append(float(x[2]))
temp.append(education_dict[x[3]])
temp.append(marital_dict[x[4]])
temp.append(income_dict[x[5]])
temp.append(card_dict[x[6]])

# 8 - 19
temp.extend([float(ele) for ele in x[7: ]])
print(f"编码之后:{temp}")

# 标准化之后
x = np.array(temp)
x = (x - mu) / sigma
print(f"标准化之后:{x}")

# 模型推理
y_pred = model.predict(X=[x])
print(f"推理结果:{y_pred}")

# 结果解析
final_result = idx2label[y_pred[0]]
print(f"最终结果:{final_result}")
return ResultEntity(result=str(final_result))

# 启动服务(不用命令启动 "fastapi dev xxx.py", 可直接 main 方法运行。)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=18019)

2、创建client.py

1
2
3
4
5
6
7
8
9
10
import requests

data = {
"x": "719808558,55,F,2,Graduate,Married,Less than $40K,Blue,43,2,4,3,1438.3,0,1438.3,0.707,886,27,0.421,0"
}

url = "http://127.0.0.1:18019/predict"

response = requests.post(url, json=data)
print(response.json()) # {'result': 'Attrited Customer'}

3、测试一把

1
2
- 启动 main.py
- 启动 client.py - 返回 {'result': 'Attrited Customer'}

day04下-特征重要性、线性模型、数据预处理

2024-11-27 08:15:16

特征重要性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
"""
1. 使用决策树来判断特征的重要性
"""
import joblib

_, data = joblib.load(filename="./all_data.lxh")
X_train, y_train, X_test, y_test = data
X_train.shape # (8101, 19)
y_train.shape # (8101,)

from sklearn.tree import DecisionTreeClassifier
dtc = DecisionTreeClassifier()
dtc.fit(X=X_train, y=y_train)
feature_importances_ = dtc.feature_importances_
feature_importances_.sort()
# 特征重要性
# array([0.00155297, 0.00208919, 0.00459715, 0.00482955, 0.00483211, 0.00651779, 0.00682284, 0.01006577, 0.01269459, 0.01499114, 0.02176497, 0.02227309, 0.04058558, 0.04407558, 0.08202801, 0.09630184, 0.14138831, 0.19550575, 0.28708376])
feature_importances_


"""
2. 分类问题的评价
- 准确率 accuracy
- 样本均衡时,可靠!
- 不同类别的样本,数量上不会相差太多!
- 样本不均衡时,特别是深度学习模型(梯度下降法)
- 这个指标有欺骗性!
- 预测对了的个数 / 总的测试个数
- 召回率 recall(宁可错杀一千,不可放过一人)
- 精准率 precision(与召回相反)
- f1-score
- 精准 召回 调和平均 f1=2RP/(R+P)
"""
y_pred = dtc.predict(X=X_test)
# 准确率 acc accuracy
# 0.9333662388943732
(y_test == y_pred).mean()

from sklearn.metrics import accuracy_score
# 0.9333662388943732
accuracy_score(y_true=y_test, y_pred=y_pred)
# 样本均衡问题
# 0.8373040365386989 84%
(y_train == 0).mean()
# 0.16269596346130108 16%
(y_train == 1).mean()

from sklearn.metrics import accuracy_score
from sklearn.metrics import recall_score
from sklearn.metrics import precision_score
from sklearn.metrics import f1_score
accuracy_score(y_true=y_test, y_pred=y_pred, ) # 0.9333662388943732
recall_score(y_true=y_test, y_pred=y_pred, average=None) # array([0.96097845, 0.77993528])
precision_score(y_true=y_test, y_pred=y_pred, average=None) # array([0.96041909, 0.78246753])
f1_score(y_true=y_test, y_pred=y_pred, average=None) # array([0.96069869, 0.78119935])


"""
3. 特征筛选
- 干掉一部分不重要的特征
- 保留一部分重要的特征
- 剩下的特征还是原来的一部分
- 优化 = 降本增效 = 裁员
"""
X_train.shape # (8101, 19)
# 抽取出 5 个最重要的特征,然后测试准确率
feature_importances_ = dtc.feature_importances_
selected_feature_idxes = feature_importances_.argsort()[::-1][:5]
selected_feature_idxes # array([16, 12, 15, 8, 17], dtype=int64)

X_train1 = X_train[:, selected_feature_idxes]
X_test1 = X_test[:, selected_feature_idxes]
dtc1 = DecisionTreeClassifier()
dtc1.fit(X=X_train1, y=y_train)
y_pred1 = dtc1.predict(X=X_test1)
acc1 = (y_pred1 == y_test).mean()
acc1 # 0.9383020730503455

# 遍历寻优
for num_features in range(1, 20, 1):
selected_feature_idxes = feature_importances_.argsort()[::-1][:num_features]
X_train1 = X_train[:, selected_feature_idxes]
X_test1 = X_test[:, selected_feature_idxes]
dtc1.fit(X=X_train1, y=y_train)
y_pred1 = dtc1.predict(X=X_test1)
acc1 = (y_pred1 == y_test).mean()
print(num_features, acc1)
# 1 0.866238894373149
# 2 0.8835143139190523
# 3 0.9106614017769002
# 4 0.9333662388943732
# 5 0.9353405725567621
# 6 0.9338598223099703
# 7 0.93928923988154
# 8 0.937314906219151
# 9 0.937314906219151
# 10 0.9353405725567621
# 11 0.9313919052319842
# 12 0.930898321816387
# 13 0.9313919052319842
# 14 0.932872655478776
# 15 0.9353405725567621
# 16 0.9353405725567621
# 17 0.9338598223099703
# 18 0.9333662388943732
# 19 0.932872655478776
# 结论:特征到一定数量,就不会太大的提升了。


"""
4. 特征融合
- 把所有特征中重要的成份抽出来
- 注入的几个新的特征中
- 新的特征不是原来的任何一部分
- 原地解散,根据实际需要,重新招人!
- PCA:主成分分析(principal components analysis)
"""
from sklearn.decomposition import PCA
pca = PCA(n_components=5)
pca.fit(X=X_train)
X_train2 = pca.transform(X=X_train)
X_test2 = pca.transform(X=X_test)
dtc2 = DecisionTreeClassifier()
dtc2.fit(X=X_train2, y=y_train)
y_pred2 = dtc2.predict(X=X_test2)
acc2 = (y_pred2 == y_test).mean()
acc2 # 0.8395853899308984

# 遍历寻优
for num_features in range(1, 20, 1):
pca = PCA(n_components=num_features)
pca.fit(X=X_train)
X_train2 = pca.transform(X=X_train)
X_test2 = pca.transform(X=X_test)
dtc2 = DecisionTreeClassifier()
dtc2.fit(X=X_train2, y=y_train)
y_pred2 = dtc2.predict(X=X_test2)
acc2 = (y_pred2 == y_test).mean()
print(num_features, acc2)
# 1 0.7408687068114511
# 2 0.7729516288252715
# 3 0.7981243830207305
# 4 0.8346495557749259
# 5 0.8361303060217177
# 6 0.8460019743336624
# 7 0.8489634748272458
# 8 0.8415597235932872
# 9 0.8469891411648569
# 10 0.8519249753208292
# 11 0.8415597235932872
# 12 0.8474827245804541
# 13 0.8657453109575518
# 14 0.8716683119447186
# 15 0.870187561697927
# 16 0.8696939782823297
# 17 0.8716683119447186
# 18 0.8840078973346496
# 19 0.8800592300098716
# 结果:老的重要特征抽取出来,转为新的特征。


"""
5. 特征分解
- FFT 快速傅立叶变换(Fast Fourier Transform Algorithm)
- SVD 奇异值分解(singular value decomposition)
- 例如:工业 信号处理,去除50Hz干扰信号 转换出去-去除-再转换回来
"""
X_train.shape# (8101, 19)
import numpy as np
U, S, Vt = np.linalg.svd(a=X_train, full_matrices=False)
from matplotlib import pyplot as plt
plt.scatter(range(len(S)), S)

from sklearn.datasets import load_breast_cancer
X, y = load_breast_cancer(return_X_y=True)
X.shape# (569, 30)
U1, S1, Vt1 = np.linalg.svd(a=X, full_matrices=False)
# 可以看出:只有前几个重要的特征,后面特征都是0
plt.plot(S1)

线性模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
"""
- 线性回归
- 逻辑回归

- 线性?
- 自变量和因变量都是一次方关系
- 因变量 y 是自变量x的线性组合
- y = kx + b(直线)
- y = k1x1 + k2x2 + b(平面)
- y = k1x1 + k2x2 + ... + knxn + b(超平面)
- 所在维度的空间(欧式空间,向量空间)内,最简单的关系(超平面)

- 回归?
- v. regress
- n. regression
- n. regressor
- 生物学概念
- 预测连续量 - (价格围绕价值上下波动)

- 线性回归?
- 标签是一个连续量
- 把标签看作是特征的线性组合
- y = w_1x_1 + w_2x_2 + ... + w_nx_n + b(假设函数)
- weight: 权重(斜率)
- bias: 偏置(截距)
- 参数:
- 可学习参数 learnable parameters(w, b)
- 超参数 hyper parameters(人为指定,不能由模型修改)
- 模型构建完成之后,参数都是随机初始化的!(不能进行精准预测的)
- 需要经过数据的训练,才能把参数确定下来!(可以进行精准预测了)

--------------------------------

迭代思想
- 迭代法:
- 问题不能一步到位的解决!!!
- 采用迭代的思想来解决!
- 1、要有随机 random 的初始化(rough start)【普适性,不是一个个例,而是一个整体】
- 2、要有一个逐步变好的策略(优化细节)
- 3、要有一个退出的条件(适时退出)
- KMeans 聚类算法
- 深度学习 - 梯度下降法
- 线性回归
- 逻辑回归
"""

"""
1. 读取数据
"""
import pandas as pd
data = pd.read_csv(filepath_or_buffer="./boston_house_prices.csv", skiprows=1)
X = data.drop(columns=["MEDV"]).to_numpy() # (506, 13)
y = data["MEDV"].to_numpy() # (506,)


"""
2. 切分&标准化
"""
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
mu = X_train.mean(axis=0)
sigma = X_train.std(axis=0)
X_train1 = (X_train - mu) / sigma
X_test1 = (X_test - mu) / sigma


"""
3. 套用模型
"""
# 模型1. 线性回归
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(X=X_train1, y=y_train)
# 权重 array([-0.97082019, 1.05714873, 0.03831099, 0.59450642, -1.8551476 , 2.57321942, -0.08761547, -2.88094259, 2.11224542, -1.87533131, -2.29276735, 0.71817947, -3.59245482])
lr.coef_
# 偏置 22.611881188118836
lr.intercept_
y_pred = lr.predict(X=X_test1)
# 平均绝对误差 Mean Absolute Error MAE
abs(y_pred - y_test).mean() # 3.8429092204444952
# 平均平方误差 Mean Squared Error MSE
# 绝对误差虽然更符合常规,但一般用平方误差,是平方,符合回归。
loss = ((y_pred - y_test) ** 2).mean() # 33.44897999767651

# 模型2. KNN回归
from sklearn.neighbors import KNeighborsRegressor
knn = KNeighborsRegressor(n_neighbors=5)
knn.fit(X=X_train1, y=y_train)
y_pred = knn.predict(X=X_test1)
loss = ((y_pred - y_test) ** 2).mean()
loss # 35.67441568627451

# 模型3. 决策树回归
from sklearn.tree import DecisionTreeRegressor
dtr = DecisionTreeRegressor()
dtr.fit(X=X_train1, y=y_train)
y_pred = dtr.predict(X=X_test1)
loss = ((y_pred - y_test) ** 2).mean()
loss # 34.27490196078431


"""
逻辑回归
- 二分类算法
- 在线性回归的基础上,增加了一个 sigmoid 激活函数
- 优化:梯度下降法 SGD: Stoc**h**astic Gradient Descent
"""
import joblib
_, [X_train, y_train, X_test, y_test] = joblib.load(filename="all_data.lxh")
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
lr.fit(X=X_train, y=y_train)
y_pred = lr.predict(X=X_test)
(y_pred == y_test).mean() # 0.9027640671273445
# 19 个 权重 [[-0.03432062 0.30570988 0.16714428 0.08224422 0.2046806 0.01729407, 0.09290679 -0.09146421 -0.71263014 0.5350626 0.53159589 -0.04987557, -0.81509068 0.02346338 -0.10912122 1.54194393 -2.69241636 -0.6710327, 0.01970949]]
lr.coef_
# 1 个 偏置 [-2.96643208]
lr.intercept_

数据预处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
"""
- 操作:
- 中心化:减均值
- 归一化:压缩到[0, 1]
- 标准化:减均值,除标准差

- 意义:
- 加速模型收敛

- 特点:
- 数据表面上被改变了!!!
- 数据内部的信息没有被改变!!!

- 信息:
- 信息是隐藏在相对大小中,而不是绝对大小中!!!(图不变的体现)
"""
import numpy as np
from matplotlib import pyplot as plt

# 原始数据 [16 5 32 16 1 0 31 17 68 62 4 41 92 43 73 41 1 41 18 61 10 54 32 17, 8 57 71 51 5 95]
scores = np.random.randint(low=0, high=101, size=(30,))
plt.plot(scores)
plt.title(label="original data")

# 中心化 [-19.43333333 -30.43333333 -3.43333333 -19.43333333 -34.43333333, -35.43333333 -4.43333333 -18.43333333 32.56666667 26.56666667, -31.43333333 5.56666667 56.56666667 7.56666667 37.56666667, 5.56666667 -34.43333333 5.56666667 -17.43333333 25.56666667, -25.43333333 18.56666667 -3.43333333 -18.43333333 -27.43333333, 21.56666667 35.56666667 15.56666667 -30.43333333 59.56666667]
scores1 = scores - scores.mean()
# 形状还是一样,只是大小改变了。
# 所有数据都减去了平均值。
plt.plot(scores1)

# 归一化 array([0.16842105, 0.05263158, 0.33684211, 0.16842105, 0.01052632, 0. , 0.32631579, 0.17894737, 0.71578947, 0.65263158, 0.04210526, 0.43157895, 0.96842105, 0.45263158, 0.76842105, 0.43157895, 0.01052632, 0.43157895, 0.18947368, 0.64210526, 0.10526316, 0.56842105, 0.33684211, 0.17894737, 0.08421053, 0.6 , 0.74736842, 0.53684211, 0.05263158, 1. ])
min_ = scores.min()
max_ = scores.max()
scores2 = (scores - min_) / (max_ - min_)
# 图的形状还是不变
plt.plot(scores2)

# 标准化 array([-0.70654418, -1.10647485, -0.12482685, -0.70654418, -1.25190419, -1.28826152, -0.16118418, -0.67018685, 1.18403717, 0.96589316, -1.14283219, 0.20238916, 2.05661317, 0.27510382, 1.36582383, 0.20238916, -1.25190419, 0.20238916, -0.63382952, 0.92953583, -0.92468819, 0.67503449, -0.12482685, -0.67018685, -0.99740285, 0.7841065 , 1.29310917, 0.56596249, -1.10647485, 2.16568517])
mu = scores.mean()
sigma = scores.std()
scores3 = (scores - mu) / sigma
# 图的形状还是不变
plt.plot(scores3)

day05上-KMeans、梯度下降法

2024-11-28 08:07:58

迭代法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 适合场景:
- `不能一次搞定`的问题!!!
- 分成`很多步`来逐步解决!!!
- KMeans 聚类算法
- Linear/Logistic Regression 线性回归和逻辑回归


- 三步走:
1、`随机` 的开始(random,rough start)
- 随机 = 普适
2、`逐步变好`的策略(optimize)
- step by step
- 每天进步一点点
- 相信累积,相信长期主义
3、`退出`条件(stop)
- 固定步数
- 误差限制

KMeans聚类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
"""
- 使用最多、最简单易用的聚类算法!!!
- clustering 聚类
- 本质:无标签的分类
- 没有标签!只有特征!
- 只根据特征来进行分类!
- 原来没有类别,根据业务需要把样本分成几类!
- 思想:物以类聚人以群分(跟KNN是一样的)
- 挨得近的看作一类,挨得远的看作另一类
- KMeans的内涵:
- K:代表分类数量,K个类
- Means:
- Mean 均值
- s 多次
- 迭代式算法跟随机的出生点是有关系的,存在一个小概率的不稳定!
- 总结:假设想分4类, 随机取4个点,靠拢;再取Mean 4个新的中心,再靠拢;...

例如:1000万人 分3类
- 脑残粉 无折扣
- 折扣 8折
- 对方粉 5折
"""
# 在数据科学中,生成假数据 fake data 是一种重要的能力!!!
# load加载数据 make生成
from sklearn.datasets import make_blobs
from matplotlib import pyplot as plt

# Generate isotropic Gaussian blobs for clustering.
X, y = make_blobs(n_samples=1000, # 1000个样本
n_features=2,# 2个特征
centers=4,# 4类
cluster_std=0.5,# 越小挨得更近
random_state=0)
plt.scatter(x=X[:, 0], y=X[:, 1], c=y)

"""
不管是有监督还是无监督学习
sklean api 高度一致的!!
"""
from sklearn.cluster import KMeans

# 实例化对象(分成4类)
km = KMeans(n_clusters=4)

# 训练(错误:环境变量问题,暂时不用管它)
km.fit(X=X, y=y)

# 4个坐标中心 [[-1.27283855 7.84651464], [ 0.92917513 4.29023124], [ 2.01942692 0.87474842], [-1.49807428 2.90164881]]
km.cluster_centers_

# 画出4类点
plt.scatter(x=X[:, 0], y=X[:, 1], c=y)

# 仅4个星
plt.scatter(x=km.cluster_centers_[:, 0],
y=km.cluster_centers_[:, 1],
c="red", marker="*", s=100 # 标记红色星星 大小100
)

线性回归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
"""
- Linear:
- 自变量x和因变量y都是一次方关系
- 因变量y是自变量x的线性组合
- 每个自变量x都乘上一个权重w,再加在一起
- weight w
- 乘到变量上,代表该变量的重要程度
- 有多少个变量,就有多少个权重
- 最后加上一个公共的偏置b
- bias b
- 加到最终结果上
- 有多少个最终的结果,就有多少个偏置
- y = wx + b
- 知道 w b 才能画出线,我们要代入数据求 w b
- 2点就可以画出直线,但所有点大部分都不在这条线上
- 上面说的只是1个点,实际是复平面 找到一个平面上的点线。--- 迭代法,步步逼近。
- f(x, y) = w_1x + w_2y + b

- Regression:
- 预测连续型数据

- 房价预测:
- 线性回归:
- 假定:房价是由特征们的线性组合得到的

--------------------------------

- 算法流程:
- 1. 随机初始化 w 和 b:
- 假设函数/模型:
- y = w1 * x1 + w2 * x2 + ... w13 * x13 + b
- 2. 从训练集中,取出一批样本 batch_X, batch_y:
- 把特征 batch_X 带入模型,得到一个预测结果 y_pred
- 此时:y_pred 是 w 和 b 的函数
- 衡量预测结果和真实结果的误差
- loss = loss_fn(y_pred, batch_y)
- 误差大,预测差; 误差小,预测好;
- loss 是 y_pred 的函数,y_pred 又是 w 和 b 的函数
- loss 是 w 和 b 的函数
- 误差loss的大小是受 w 和 b 的影响
- 误差小,模型好
- 数学问题:
- 求函数的最/极小值
- 当 w 和 b 是多少的时候,loss 可以取得最小值?
- 综上所述,模型优化的问题,就变成了一个函数求最小值的问题!
--------------------------------
如何求函数的最小值?
- 求 y = F(x) 的最小值?

- 理论数学:
1、求导数/偏导
2、令导数/偏导等于零
3、解方程/组,得到疑似结果,再做一进步结果验证
(工程不可行)
(样本不在模型上,不在线/面上,在模型周围!!!)

- 工程上:
- 迭代法
- (随机)梯度下降法 SGD Stochastic Gradient Descent
"""

# 1. 线性关系
# $ f(x)=2x+1 $
# $ f(x, y) = 3x + 6y -8 $
# $ y = w_1x_1 + w_2x_2 + ... + w_nx_n + b $


# 2. 读取数据
import pandas as pd
data = pd.read_csv(filepath_or_buffer="boston_house_prices.csv", skiprows=1)
data = data.to_numpy()
X = data[:, :-1]
y = data[:, -1]
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
# 预处理(标准化 / 规范化 / norm / standard)
mu = X_train.mean(axis=0)
sigma = X_train.std(axis=0)
X_train = (X_train - mu) / sigma
X_test = (X_test - mu) / sigma

# $ price = w_1*feature_1 + w_2 * feature_2 + ... + w_{13} * feature_{13} + b $
# 价格 与 所有特征 呈现线性关系


# 3. 使用 sklearn 解决问题
from sklearn.linear_model import LinearRegression
# 假设函数
lr = LinearRegression()
# 寻找和固定 w 和 b 的过程
lr.fit(X=X_train, y=y_train)
# 13个特征 有 13weight
# 1 bias
lr.coef_ # weight [-0.97082019 1.05714873 0.03831099 0.59450642 -1.8551476 2.57321942, -0.08761547 -2.88094259 2.11224542 -1.87533131 -2.29276735 0.71817947, -3.59245482]
lr.intercept_ # bias 22.611881188118808
lr.predict(X=X_test)

梯度下降法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import numpy as np
from matplotlib import pyplot as plt
# 定义函数
def fn(x):
return x ** 2
x = np.linspace(start=-5, stop=5, num=100)
# 绘制图形
plt.plot(x, fn(x))
plt.grid()
# 求导函数
def dfn(x):
return 2 * x

# 走一个固定的步数 - 执行1000次迭代来逼近最优解
steps = 1000
# 把步子压得小一点
# 学习率=0.01(1e-2是科学计数法表示0.01)
# 学习率太小,模型要很多迭代才能收敛到最优解;
# 反之,太大,则可能导致模型在最小值附近震荡,甚至无法收敛。
learning_rate = 1e-2

# 1,随机的开始
x = np.random.randint(low=-1000, high=1001, size=(1,))
print(f"x的初始值: {x}")
# 2,迭代优化
for step in range(steps):
# 梯度下降法(减倒数 => 梯度下降)
x = x - learning_rate * dfn(x)
# print(f"优化{step+1}步后, x的值: {x}")
print(f"x的最终值: {x}")
# x的初始值: [714]
# 优化1步后, x的值: [699.72]
# 优化2步后, x的值: [685.7256]
# 优化3步后, x的值: [672.011088]
# 优化4步后, x的值: [658.57086624]
# 优化5步后, x的值: [645.39944892]
# 优化6步后, x的值: [632.49145994]
# 优化7步后, x的值: [619.84163074]
# 优化8步后, x的值: [607.44479812]
# ...
# 优化994步后, x的值: [1.35649267e-06]
# 优化995步后, x的值: [1.32936281e-06]
# 优化996步后, x的值: [1.30277556e-06]
# 优化997步后, x的值: [1.27672005e-06]
# 优化998步后, x的值: [1.25118564e-06]
# 优化999步后, x的值: [1.22616193e-06]
# 优化1000步后, x的值: [1.20163869e-06]
# x的最终值: [1.20163869e-06]
# 注意,这里是科学计数法
# 1.20163869e-06
# 1.20163869 * 10 ** -6
# 1.20163869 * 0.000001
# 0.00000120163869

day05下-深度学习框架PyTorch

2024-11-29 07:53:15

深度学习框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
"""
1. 深度学习框架
- TensorFlow
- 上一代框架
- 静态图起家
- 深度学习前期很重要
- 执行性能高,但是模型搭建和调试困难

- PyTorch
- 主流,特别是大模型时代,市场占有率 `100%`
- 动态图起家
- 执行性能低,但是编程效率高
- 直接把模型的搭建,变成了Python编程,消除了一切难度
- 稳如老狗
- API 从 0.x 到 现在,几乎不变
- 升级都是内功修炼

- PaddlePaddle
- 百度 - 抄袭 TensorFlow PyTorch

--------------------------------

2. 深度学习给我带来了什么?
- 深度学习框架:是一个针对深度学习的科学计算库
- NumPy:是一个通用科学计算库
- 历史演进的角度来说:
- 深度学习框架是一个 NumPy++
1、实现了 NumPy 的所有功能、使用习惯、命名等!
2、实现了自动求导!(复杂函数求梯度,变得简单,自己写很麻烦)
3、实现常用的模块(层,优化器....)

sklearn所有算法实现好的包,直接调用即可。
深度学习是一个个积木,

--------------------------------

3. 深度学习本质:
- 统计学项目:
- 想去研究总体/全量的一些情况,但是不能直接去研究
- 退而求其次,对总体进行采样,试图用采样得到的数据来估计总体
- 人工智能的本质是以小博大!
- 使用样本来估计总体!

- 对`样本`的统计量:
- 均值 mu = sum(x) / len(x)
- 方差 sum((x - mu) ** 2) / len(x)
- 标准差 (sum((x - mu) ** 2) / len(x)) ** 0.5

- 对`总体`的统计量:
- 均值 mu = sum(x) / len(x)
- 方差 sum((x - mu) ** 2) / (len(x) - 1)
- 标准差 (sum((x - mu) ** 2) / (len(x) - 1)) ** 0.5

- 思想来源!
- 实际工作中,数据量非常大,两者没什么区别!!!随便用即可!!!
- 有些书,是思想,直接看学不会,要实践过再去看才有收获。
- 有些书,你看说了一堆,也都对,但实际无什么实用价值。
--------------------------------
"""

PyTorch-安装、简单使用

PyTorch - 我的安装笔记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
"""
- 安装:
- https://pytorch.org/
- CPU版:
- pip install torch torchvision
- GPU版:
- 前提:
1、英伟达 + 独立显卡(显存 >= 4G)
2、显卡驱动最新
- 安装:
- pip install torch torchvision --index-url https://download.pytorch.org/whl/cu124
- 测试:
- import torch
- torch.__version__
"""


"""
1. 测试安装
"""
import torch
torch.__version__ # '2.5.1'
# cuda == GPU
# CMD 查看 GPU 'nvidia-smi'
torch.cuda.is_available() # False
# 我笔记本无GPU AssertionError: Torch not compiled with CUDA enabled
torch.cuda.get_device_name()


"""
2. 张量 基本计算
"""
import random
import numpy as np
import torch
scores = [random.randint(a=0, b=100) for _ in range(30)]
arr = np.array(scores)
t = torch.tensor(data=scores, dtype=torch.float32)

type(scores) # list
type(arr) # numpy.ndarray
type(t) # torch.Tensor

t # tensor([62., 69., 83., 2., 47., 77., 64., 78., 93., 51., 22., 84., 0., 16., 24., 22., 17., 68., 60., 76., 62., 38., 38., 24., 96., 94., 71., 98., 20., 35.])
arr # array([62, 69, 83, 2, 47, 77, 64, 78, 93, 51, 22, 84, 0, 16, 24, 22, 17, 68, 60, 76, 62, 38, 38, 24, 96, 94, 71, 98, 20, 35])
scores # [62, 69, 83, 2, 47, 77, 64, 78, 93, 51, 22, 84, 0, 16, 24, 22, 17, 68, 60, 76, 62, 38, 38, 24, 96, 94, 71, 98, 20, 35]

arr.min()
t.min()

arr.max()
t.max()

arr.mean()
t.mean()

arr.sum()
t.sum()

# NumPy 默认求的是 样本方差
arr.var(), arr.var(ddof=1)
# PyTorch 默认求的是 总体方差
t.var(), t.var(correction=0)

arr.size
t.numel()

arr.ndim
t.ndim

np.arange(12).reshape(3, 4)
torch.arange(12).reshape(3, 4)

np.ones(shape=(2, 3))
torch.ones(2, 3)

np.linalg.norm(arr)
torch.linalg.norm(t)

np.linspace(start=-5, stop=5, num=100)
torch.linspace(start=-5, end=5, steps=100)


"""
3. GPU的使用
"""
# 检测GPU是否可用
device = "cuda" if torch.cuda.is_available() else "cpu"
device # 'cpu'

t1 = torch.randn(2, 3)
t2 = torch.randn(2, 3, device=device)

t1 + t2
t1.to(device=device) + t2
t2.cpu() + t1
# t1.cuda() # 我笔记本无GPU AssertionError: Torch not compiled with CUDA enabled

PyTorch-自动求导

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
"""
1. 自动求导
$ y = x^2$
"""
# 原函数
def fn(x):
return x ** 2
# 导函数
def dfn(x):
return 2 * x
# 数学:常量
x1 = torch.randn(1) # tensor([0.1091])

# 数学:变量 --- 梯度=gradient=grad
# x2是一个变量,这个变量当前取值为 -0.4356
x2 = torch.randn(1, requires_grad=True) # tensor([-0.4356], requires_grad=True)
# 当前值 tensor([-0.4356])
x2.data

# 梯度值 --- 还未有导数值
print(x2.grad) # None

y = x2 ** 2 # tensor([0.1897], grad_fn=<PowBackward0>)

# 反向传播 --- 填充 x2.grad --- 根据函数反推x2的导数值(求梯度)
y.backward()

# 梯度值 --- 已有导数值
x2.grad # tensor([-0.8712])

# 清空梯度
x2.grad.zero_()
x2.grad # tensor([0.])


"""
2. PyTorch 自动求导实现梯度下降法
"""
steps = 1000
learning_rate = 1e-2 # 0.01(1 乘以 10的-2次方)

x = torch.randint(low=-1000, high=1001, size=(1,), dtype=torch.float32, requires_grad=True)
print(f"x初始值为:{x}")
for step in range(steps):
# 1, 正向传播
y = x ** 2
# 2, 反向传播 --- 执行了这个之后,x.grad 就可以获取到导数值(我们无需定义导函数)
y.backward()
# 3, 梯度下降
x.data -= learning_rate * x.grad
# 4, 清空梯度
x.grad.zero_()
print(f"优化了{step + 1}步,x为:{x}")
print(f"x最终值为:{x}")
# x初始值为:tensor([442.], requires_grad=True)
# 优化了1步,x为:tensor([433.1600], requires_grad=True)
# 优化了2步,x为:tensor([424.4968], requires_grad=True)
# 优化了3步,x为:tensor([416.0069], requires_grad=True)
# 优化了4步,x为:tensor([407.6867], requires_grad=True)
# 优化了5步,x为:tensor([399.5330], requires_grad=True)
# ...
# 优化了995步,x为:tensor([8.2294e-07], requires_grad=True)
# 优化了996步,x为:tensor([8.0648e-07], requires_grad=True)
# 优化了997步,x为:tensor([7.9035e-07], requires_grad=True)
# 优化了998步,x为:tensor([7.7454e-07], requires_grad=True)
# 优化了999步,x为:tensor([7.5905e-07], requires_grad=True)
# 优化了1000步,x为:tensor([7.4387e-07], requires_grad=True)
# x最终值为:tensor([7.4387e-07], requires_grad=True)
# ===> 深度学习的内核

PyTorch-线性回归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import torch

# 定义参数
# 13: weight 权重(13行 1列)
w = torch.randn(13, 1, requires_grad=True)
# 1: bias 偏置(1行 1列)
b = torch.randn(1, 1, requires_grad=True)

# 定义线性回归的处理逻辑
# @=内积
def model(x):
return x @ w + b

# 转换数据
X_train = torch.tensor(data=X_train, dtype=torch.float32)
y_train = torch.tensor(data=y_train.reshape(-1, 1), dtype=torch.float32)

steps = 3000
learning_rate = 1e-3 # 0.001(1 乘以 10的-3次方) --- 步子小一点,慢,但优

for step in range(steps):
# 1,正向传播
y_pred = model(X_train)
# 2, 计算损失 mse --- 均方误差(mean square error)
loss = ((y_train - y_pred) ** 2).mean()
# 3, 反向传播
loss.backward()
# 4, 优化一步
w.data -= learning_rate * w.grad
b.data -= learning_rate * b.grad
# 5, 清空梯度
w.grad.zero_()
b.grad.zero_()

def predict(X_test):
# 转张量
X_test = torch.tensor(data=X_test, dtype=torch.float32)

# 预测
with torch.no_grad():
y_pred = model(X_test)
return y_pred

y_pred = predict(X_test)

y_pred = y_pred.view(-1).numpy()
mse = ((y_pred - y_test) ** 2).mean()

PyTorch-逻辑回归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
"""
1. 加载数据
$ y=w_1x_1 + w_2x_2 + ... + w_n x_n + b $
"""
# 加载数据
from sklearn.datasets import load_breast_cancer
X, y = load_breast_cancer(return_X_y=True)
# 拆分数据
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
# 标准化
mu = X_train.mean(axis=0)
sigma = X_train.std(axis=0)
X_train = (X_train - mu) / sigma
X_test = (X_test - mu) / sigma


"""
2. 建模
"""
import torch
# 定义权重和偏置
w = torch.randn(30, 2, requires_grad=True)
b = torch.randn(1, 2, requires_grad=True)

def model(X):
return X @ w + b

steps = 1000
learning_rate = 1e-2

# 转张量 -> 转为 tensor 类型
X_train = torch.tensor(data=X_train, dtype=torch.float32)

# 类别需要使用one-hot编码
def one_hot(y):
result = torch.stack(tensors=[torch.eye(2)[label] for label in y], dim=0)
return result

# 模型的原始输出使用softmax模拟概率
def softmax(x):
return torch.exp(x) / torch.exp(x).sum()

# 衡量分类问题的误差
def cross_entropy(y_pred, y_true):
pass

day06-逻辑回归

2024-12-02 08:10:32

交叉熵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
"""
1. 分类模型
- 输入:
- 由多少个特征决定!
- 30个特征
- 输出:
- 由有多少的类别决定!
- 2分类

--------------------------------

人工智能本质:y=f(x) 把x映射到y。
把x数字化,做数据科学计算。
可学习变量 w,b 与 变量 x 做计算 就是处理逻辑。
算法思想:把输出当做输入的线性组合。
"""


"""
2. 信息熵
- information entropy
- 信息熵,简称熵
- 代表系统的混乱程度
- 分类问题:
- 类别越多,各类别越均衡,系统越乱,熵越大!
--------------------------------
"""
import numpy as np
probs = np.array([0.999, 0.001, 0.001])
entropy = probs @ np.log2(1 / probs)
entropy # 0.02137354202212335


"""
3. 交叉熵
- cross entropy
- 分类问题最重要的损失函数
- 衡量预测结果和真实分类之间的误差
- 本质:从概率分布的角度来衡量损失

假设总共有3类:
- 0, 1, 2
- 真实标签:2
"""
# 真实标签
y_true = 2
# 预测结果(原始输出)
y_pred = np.array([-0.32, 1.90, 1.98])

# 1,把真实标签变为概率分布(one-hot)
y_true = np.array([0.0, 0.0, 1.0]) # [0. 0. 1.]
# 2,把模型原始预测输出变为概率分布(softmax)
y_pred = np.exp(y_pred) / np.exp(y_pred).sum() # [0.0495503 0.45622599 0.49422371]
# 3. 交叉熵 cross entropy
ce = y_true @ np.log(1 / y_pred) # 0.704767003228585

逻辑回归-v1-底层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
"""
1. 加载数据
"""
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
# 加载数据
X, y = load_breast_cancer(return_X_y=True)
# 切分数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)


"""
2. 数据预处理
标准化 - 对特征进行标准化处理
"""
# 提取需要的参数
mu = X_train.mean(axis=0)
sigma = X_train.std(axis=0)
# 预处理特征
X_train = (X_train - mu) / sigma
X_test = (X_test - mu) / sigma


"""
3. 构建模型
"""
import torch
# 可学习参数(learnable parameter)
# 定义权重和偏置 X: [batch_size, 30]
# 权重的初始化:由高斯分布来初始化 (30行 2列)
w = torch.randn(30, 2, dtype=torch.float32, requires_grad=True)
# 偏置的初始化:由 0.0 来初始化 (1行 2列)
b = torch.randn(1, 2, dtype=torch.float32, requires_grad=True)
w, b

# 定义模型的处理逻辑 - 算法思想
def model(X):
return X @ w + b


"""
4. 准备训练
"""
# 训练步数
steps = 2000
# 学习率: 适当压缩偏导数,防止梯度爆炸
learning_rate = 1e-2

# 数据转张量(ndarray -> tensor)
X_train = torch.tensor(data=X_train, dtype=torch.float32)
X_test = torch.tensor(data=X_test, dtype=torch.float32)
y_train = torch.tensor(data=y_train, dtype=torch.long)
y_test = torch.tensor(data=y_test, dtype=torch.long)

def get_cross_entropy(y_pred, y_true):
"""
衡量分类问题的误差
- 批量的交叉熵 - 每个样本的损失的平均值
"""
# 第 1 步:真实标签 转 one - hot
y_true = torch.eye(2)[y_true]
# 第 2 步:原始输出 转 概率
y_pred = torch.exp(y_pred) / torch.exp(y_pred).sum(dim=1, keepdim=True) + 1e-9
# 第 3 步:求交叉熵
cross_entropy = (y_true * torch.log(1 / y_pred)).sum(dim=1)
# 第 4 步:求平均交叉熵
cross_entropy = cross_entropy.mean()
# 返回即可
return cross_entropy


"""
4. 过程监控 - 准确率
"""
def get_acc(X, y):
with torch.no_grad():
# 1,正向传播
y_pred = model(X=X)
# 2,解析结果
y_pred = y_pred.argmax(dim=1)
# 3, 计算准确率
acc = (y == y_pred).to(dtype=torch.float32).mean().item()
return acc


"""
5. 训练过程
"""
def train():
# 训练前,测试一下准确率
train_acc = get_acc(X=X_train, y=y_train)
test_acc = get_acc(X=X_test, y=y_test)
print(f"开始训练之前,train_acc: {train_acc}, test_acc: {test_acc}")

# 开始训练
for step in range(steps):
# 1, 正向传播
y_pred = model(X=X_train)
# 2,计算损失
loss = get_cross_entropy(y_pred=y_pred, y_true=y_train)
# 3, 反向传播
loss.backward()
# 4, 优化一步
w.data -= learning_rate * w.grad
b.data -= learning_rate * b.grad

# 5,清空梯度
w.grad.zero_()
b.grad.zero_()

# 6, 模型评估
train_acc = get_acc(X=X_train, y=y_train)
test_acc = get_acc(X=X_test, y=y_test)
print(f"训练了{step + 1}轮,train_acc: {train_acc}, test_acc: {test_acc}")

train()
# 开始训练之前,train_acc: 0.4703296720981598, test_acc: 0.41228070855140686
# 训练了1轮,train_acc: 0.48351648449897766, test_acc: 0.429824560880661
# 训练了2轮,train_acc: 0.49450549483299255, test_acc: 0.429824560880661
# 训练了3轮,train_acc: 0.5010989308357239, test_acc: 0.4385964870452881
# 训练了4轮,train_acc: 0.5120879411697388, test_acc: 0.44736841320991516
# ...
# 训练了1994轮,train_acc: 0.9604395627975464, test_acc: 0.9473684430122375
# 训练了1995轮,train_acc: 0.9604395627975464, test_acc: 0.9473684430122375
# 训练了1996轮,train_acc: 0.9604395627975464, test_acc: 0.9473684430122375
# 训练了1997轮,train_acc: 0.9604395627975464, test_acc: 0.9473684430122375
# 训练了1998轮,train_acc: 0.9604395627975464, test_acc: 0.9473684430122375
# 训练了1999轮,train_acc: 0.9604395627975464, test_acc: 0.9473684430122375
# 训练了2000轮,train_acc: 0.9604395627975464, test_acc: 0.9473684430122375

逻辑回归-v2-框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
"""
(同v1)
1. 加载数据
"""
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
# 加载数据
X, y = load_breast_cancer(return_X_y=True)
# 切分数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)



"""
(同v1)
2. 数据预处理
标准化 - 对特征进行标准化处理
"""
# 提取需要的参数
mu = X_train.mean(axis=0)
sigma = X_train.std(axis=0)
# 预处理特征
X_train = (X_train - mu) / sigma
X_test = (X_test - mu) / sigma


"""
(改造)
3. 构建模型
"""
import torch
from torch import nn
# 线性层
model = nn.Linear(in_features=30, out_features=2)


"""
(改造)
4. 准备训练
"""
# 训练步数
steps = 200
# 优化器(SGD 随机梯度下降, 封装:减梯度、清空梯度)
optimizer = torch.optim.SGD(params=model.parameters(), lr=1e-3)
# 损失函数
loss_fn = nn.CrossEntropyLoss()

# 数据转张量
X_train = torch.tensor(data=X_train, dtype=torch.float32)
X_test = torch.tensor(data=X_test, dtype=torch.float32)
y_train = torch.tensor(data=y_train, dtype=torch.long)
y_test = torch.tensor(data=y_test, dtype=torch.long)


"""
5. 过程监控 - 准确率
"""
def get_acc(X, y):
with torch.no_grad():
# 1,正向传播
y_pred = model(X)
# 2,解析结果
y_pred = y_pred.argmax(dim=1)
# 3, 计算准确率
acc = (y == y_pred).to(dtype=torch.float32).mean().item()
return acc


"""
5. 训练过程
"""
def train():
# 训练前,测试一下准确率
train_acc = get_acc(X=X_train, y=y_train)
test_acc = get_acc(X=X_test, y=y_test)
print(f"开始训练之前,train_acc: {train_acc}, test_acc: {test_acc}")

# 开始训练
for step in range(steps):
# 1, 正向传播
y_pred = model(X_train)
# 2,计算损失
loss = loss_fn(y_pred, y_train)
# 3, 反向传播
loss.backward()
# 4, 优化一步
optimizer.step()
# 5,清空梯度
optimizer.zero_grad()

# 6, 模型评估
train_acc = get_acc(X=X_train, y=y_train)
test_acc = get_acc(X=X_test, y=y_test)
print(f"训练了{step + 1}轮,train_acc: {train_acc}, test_acc: {test_acc}")
train()
# 开始训练之前,train_acc: 0.7472527623176575, test_acc: 0.7280701994895935
# 训练了1轮,train_acc: 0.7472527623176575, test_acc: 0.7368420958518982
# 训练了2轮,train_acc: 0.7494505643844604, test_acc: 0.7368420958518982
# 训练了3轮,train_acc: 0.7494505643844604, test_acc: 0.7368420958518982
# 训练了4轮,train_acc: 0.7516483664512634, test_acc: 0.7368420958518982
# ...
# 训练了1996轮,train_acc: 0.9670329689979553, test_acc: 0.9122806787490845
# 训练了1997轮,train_acc: 0.9670329689979553, test_acc: 0.9122806787490845
# 训练了1998轮,train_acc: 0.9670329689979553, test_acc: 0.9122806787490845
# 训练了1999轮,train_acc: 0.9670329689979553, test_acc: 0.9122806787490845
# 训练了2000轮,train_acc: 0.9670329689979553, test_acc: 0.9122806787490845


"""
6. 模型保存
"""
# × 模型整体保存和加载 - 不推荐
# torch.save(obj=model, f="model.lxh")
# m = torch.load(f="model.lxh")

# √ 参数和网络分离式保存 - 骨肉分离
# 保存权重
torch.save(obj=model.state_dict(), f="model.pt")
# 加载模型:构建模型(随机初始化)
m = nn.Linear(in_features=30, out_features=2)
m.load_state_dict(state_dict=torch.load(f="model.pt", weights_only=True))


"""
7. 推理流程
"""
# 初始化模型
m = nn.Linear(in_features=30, out_features=2)
# 加载训练好的权重
m.load_state_dict(state_dict=torch.load(f="model.pt", weights_only=True))

# 推理流程
def predict(X):
# 类型校验
if not isinstance(X, torch.Tensor):
X = torch.tensor(data=X, dtype=torch.float32)
# 数据结构判断 [batch_size, num_features]
if X.ndim !=2 or X.size(1) != 30:
raise ValueError("输入数据有误!!!")
# 模型推理
y_pred = m(X)
y_pred = y_pred.argmax(dim=1)
return y_pred

predict(X=X_test)
y_test # tensor([0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,..., 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1])

疑问点

线性回归 (Linear Regression)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
"""
1. 目标
预测连续值:线性回归用于预测连续的数值输出,例如房价、温度等。

2. 数学模型
假设函数: ( y = w_1x_1 + w_2x_2 + \ldots + w_nx_n + b )
损失函数:通常使用均方误差 (MSE) 作为损失函数,即 ( \text{MSE} = \frac{1}{m} \sum_{i=1}^{m} (y_i - \hat{y}_i)^2 )
"""
import torch
import torch.nn as nn
import torch.optim as optim

# 生成数据
X = torch.randn(100, 10) # 100个样本,每个样本10个特征
y = torch.randn(100, 1) # 100个样本,每个样本1个连续值输出

# 定义模型
model = nn.Linear(in_features=10, out_features=1)

# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 训练模型
for epoch in range(100):
# 前向传播
y_pred = model(X)
# 计算损失
loss = criterion(y_pred, y)
# 反向传播
optimizer.zero_grad()
loss.backward()
# 更新参数
optimizer.step()
# 打印损失
if (epoch + 1) % 10 == 0:
print(f'Epoch [{epoch+1}/100], Loss: {loss.item():.4f}')

# Epoch [10/100], Loss: 1.6129
# Epoch [20/100], Loss: 1.4541
# Epoch [30/100], Loss: 1.3494
# Epoch [40/100], Loss: 1.2791
# Epoch [50/100], Loss: 1.2313
# Epoch [60/100], Loss: 1.1983
# Epoch [70/100], Loss: 1.1754
# Epoch [80/100], Loss: 1.1593
# Epoch [90/100], Loss: 1.1480
# Epoch [100/100], Loss: 1.1400

逻辑回归 (Logistic Regression)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
"""
1. 目标
分类:逻辑回归用于二分类或多分类问题,例如判断一封邮件是否是垃圾邮件、肿瘤是否恶性等。

2. 数学模型
假设函数: ( z = w_1x_1 + w_2x_2 + \ldots + w_nx_n + b )
激活函数: ( y = \sigma(z) ),其中 ( \sigma(z) = \frac{1}{1 + e^{-z}} ) 是sigmoid函数
损失函数:通常使用交叉熵损失函数,即 ( \text{CE} = -\frac{1}{m} \sum_{i=1}^{m} [y_i \log(\hat{y}_i) + (1 - y_i) \log(1 - \hat{y}_i)] )
"""
import torch
import torch.nn as nn
import torch.optim as optim

# 生成数据
X = torch.randn(100, 10) # 100个样本,每个样本10个特征
y = torch.randint(0, 2, (100,)) # 100个样本,每个样本1个二分类标签

# 定义模型
model = nn.Sequential(
nn.Linear(in_features=10, out_features=1),
nn.Sigmoid()
)

# 定义损失函数和优化器
criterion = nn.BCELoss() # 二分类交叉熵损失
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 训练模型
for epoch in range(100):
# 前向传播
y_pred = model(X).squeeze()
# 计算损失
loss = criterion(y_pred, y.float())
# 反向传播
optimizer.zero_grad()
loss.backward()
# 更新参数
optimizer.step()
# 打印损失
if (epoch + 1) % 10 == 0:
print(f'Epoch [{epoch+1}/100], Loss: {loss.item():.4f}')

# Epoch [10/100], Loss: 0.7217
# Epoch [20/100], Loss: 0.7186
# Epoch [30/100], Loss: 0.7156
# Epoch [40/100], Loss: 0.7128
# Epoch [50/100], Loss: 0.7101
# Epoch [60/100], Loss: 0.7076
# Epoch [70/100], Loss: 0.7052
# Epoch [80/100], Loss: 0.7029
# Epoch [90/100], Loss: 0.7007
# Epoch [100/100], Loss: 0.6987

线性回归 VS 逻辑回归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
关键区别
1. 目标:
线性回归:预测连续值。
逻辑回归:分类问题,通常是二分类或多分类。

2. 假设函数:
线性回归:直接使用线性组合 ( y = w_1x_1 + w_2x_2 + \ldots + w_nx_n + b )。
逻辑回归:使用线性组合 ( z = w_1x_1 + w_2x_2 + \ldots + w_nx_n + b ),然后通过sigmoid函数 ( y = \sigma(z) ) 将输出映射到0到1之间。

3. 损失函数:
线性回归:均方误差 (MSE)。
逻辑回归:交叉熵损失 (CE)。

4. 输出:
线性回归:直接输出连续值。
逻辑回归:输出概率值,通常需要阈值(如0.5)来确定分类结果。

--------------------------------

代码中的具体体现
1. 模型定义:
线性回归:直接使用 nn.Linear。
逻辑回归:使用 nn.Linear 后接 nn.Sigmoid。
2. 损失函数:
线性回归:使用 nn.MSELoss。
逻辑回归:使用 nn.BCELoss。
3. 标签类型:
线性回归:标签是连续值,通常为浮点数。
逻辑回归:标签是分类标签,通常为整数(0或1)。
4. 前向传播:
线性回归:直接输出模型的预测值。
逻辑回归:输出模型的预测值后,需要通过 squeeze 去除多余的维度,并将标签转换为浮点数。

损失值问题

1
2
3
4
5
6
1. 损失值的范围:
- 线性回归的MSE损失值通常较大,因为它是平方差的平均值。
- 逻辑回归的BCE损失值通常较小,因为它是对数损失的平均值。
2. 损失值的变化趋势:
- 线性回归的损失值通常随着训练的进行逐渐减小,但可能不会完全降到0,因为真实数据中可能存在噪声。
- 逻辑回归的损失值也随着训练的进行逐渐减小,但最终会趋于稳定,因为模型在分类任务上达到一定的准确率后,损失值难以进一步降低。

为什么大写X,小写y?

2024-12-03 08:40:03
https://tongyi.aliyun.com/qianwen/?sessionId=d2a8f97a0f184b6388e2a509cb57e52c

1
2
3
4
5
6
# X=特征 y=标签
在Python中,特别是在机器学习和数据科学领域,使用大写X和小写y来表示数据集中的特征(features)与标签(labels)是一种约定俗成的习惯。

# X 输入多维, y 输出1维
在数学、统计学以及机器学习的文献中,经常使用大写字母如X来表示矩阵或向量形式的输入变量(即特征),而用小写字母如y来表示输出变量(即标签)。
这是因为输入变量往往包含多个特征,可以看作是一个多维的数据结构;而输出变量通常是单个值或者是低维度的。

什么是梯度下降?

2024-12-03 08:49:57
https://tongyi.aliyun.com/qianwen/?sessionId=1846b5bd371d4ac998dcf0820d74525f

举个栗子
机器学习 => 最小化损失函数 => 模型的最佳参数

1
2
3
4
5
6
7
8
9
10
11
12
13
# 说明
想象你站在一座山的山顶上,目标是到达山谷的最低点。
但是,因为大雾你看不清整个山脉的样子,只能看到脚下的一小片区域。

# 过程
为了尽快下山,你可以采取如下策略:
1、查看周围:检查你当前所在位置的坡度。这相当于计算当前位置的梯度(或导数)。
2、选择方向:朝最陡峭的下坡方向走一小步。这相当于用梯度乘以一个小的学习率,并从当前位置减去这个值来更新你的位置。
3、重复步骤:不断重复上述过程,直到达到谷底或者坡度变得非常平缓,表明你已经接近底部。

# 总结
在这个比喻中,“最陡峭的下坡方向”就是梯度的负方向,而“走一小步”的大小由学习率决定。
随着不断迭代,你会逐渐逼近山谷的最低点,就像梯度下降法帮助我们找到函数的最小值一样。

day07-集成学习

机器学习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
- 机器:machine  不是机器设备,而是 "计算机软硬件组织"
- 学习:learn
- 分类(根据是否有标签):
- 有监督学习 supervised learning
- 样本:有特征 X ,也有对应的标签 y
- 分类算法:预测一个离散量
- 逻辑回归
- KNN回归
- 朴素贝叶斯
- 决策树
- 支持向量机
- 集成学习
- 回归算法:预测一个连续量
- 线性回归
- KNN回归
- 决策树回归
- 支持向量机回归
- 集成学习
- 无监督学习 unsupervised learning
- 样本:有特征 X,但是没有标签 y
- 聚类算法
- KMeans
- 特征预处理类算法:
- 降维算法:
- PCA
- SVD
- 中心化
- x - mu
- 标准化
- (x - mu) / sigma
- 归一化
- (x - _min)/(_max - _min)

- 总结算法的思想:
- 打造一个独立、强大的算法!!!
- 单个算法打天下!!!
- 单个算法解决问题!!!
- 个人英雄主义!孤单新英雄!

集成学习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
- Ensemble Learning
- 群狼打败猛虎的策略!!
- 是一种管理理念!!
- 招募一批二流货色,组合成一个强大的团队,干掉那个一流货色!!!
- 三个臭皮匠,干掉诸葛亮!!!
- 使用一系列的弱评估器(分类器和回归器),通过头脑风暴,干掉一个强分类器!!!
- 要素:
- 一批弱分类器?
- 组合策略?

- 集成学习思想的变体:
- DropOut
- MoE:Mixture of Experts 混合专家

- 策略:
- Voting:投票策略(少数服从多数)
- Bagging:对数据采样策略
- Stacking:双阶段策略
- Boosting:错题本思想

- 实用算法:
- 随机森林算法
- 小批次训练
- XGBoost(Kaggle比赛大杀器)
- LightGBM
- 干掉 XGBoost
- 微软维护的,顶流
- 机器学习中的大数据算法!!!百万数据量

- 结构化数据/表格类数据 tabular data
- 最终还是得靠机器学习
- 集成学习
- 深度学习算法可以做,但是效果不如集成学习!

集成学习-代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
"""
1. Voting 思想
- 算法不同,数据相同
--------------------------------
Soft Voting/Majority Rule classifier for unfitted estimators.

unfitted 未拟合(主动构建的弱分类器)
- 主动构建一批弱鸡分类器
- 集成学习策略

overfit 过拟合(训练大了,入戏太深,书呆子,把训练集上的错误也学习了)
- 训练集表现非常好
- 测试集表现非常差
- 模型被训练废了,不能用了!

underfit 欠拟合(训练不够,没有充分学习全部有效知识)
- 训练集表现不够好
- 测试集表现也不够好
- 原因:训练不够!!!
"""
import numpy as np
from sklearn.ensemble import VotingClassifier
from sklearn.ensemble import VotingRegressor
from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import RandomForestClassifier, VotingClassifier


X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
y = np.array([1, 1, 1, 2, 2, 2])

# 三个模型 ===> 把鸡蛋放在多个篮子中,平行赛马
clf1 = LogisticRegression(random_state=1)
clf2 = RandomForestClassifier(n_estimators=50, random_state=1)
clf3 = GaussianNB()
eclf1 = VotingClassifier(
estimators=[('lr', clf1), ('rf', clf2), ('gnb', clf3)],
voting='hard'
)
eclf1 = eclf1.fit(X, y)
print(eclf1.predict(X)) # [1 1 1 2 2 2]


"""
2. Bagging思想
- bootstrap aggregating
- 算法相同,数据不同
--------------------------------
Bagging分类器是一种集成元分类器,它在原始数据集的随机子集上拟合每个基本分类器,
然后将它们的单独预测(通过投票或平均)汇总以形成最终预测。

这样的元分类器通常可以用作减少黑盒分类器(例如,决策树)方差的方法,
方法是在其构造过程中引入随机化,然后从中进行集成。
"""
from sklearn.ensemble import BaggingClassifier
from sklearn.ensemble import BaggingRegressor
# base estimator
from sklearn.svm import SVC
from sklearn.ensemble import BaggingClassifier
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=100,
n_features=4,
n_informative=2,
n_redundant=0,
random_state=0,
shuffle=False)

# n_estimators=10 分成10份
clf = BaggingClassifier(estimator=SVC(), n_estimators=10, random_state=0).fit(X, y)
clf.predict([[0, 0, 0, 0]]) # array([1])


"""
3. Stacking 双阶段思想
- 整合策略
--------------------------------
堆叠泛化包括堆叠单个分类器的输出,并使用分类器计算最终预测。
"""
from sklearn.ensemble import StackingClassifier
from sklearn.ensemble import StackingRegressor
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import LinearSVC
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
from sklearn.ensemble import StackingClassifier
from sklearn.model_selection import train_test_split

X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)

estimators = [
('rf', RandomForestClassifier(n_estimators=10, random_state=42)),
('svr', make_pipeline(StandardScaler(), LinearSVC(dual="auto", random_state=42)))
]
clf = StackingClassifier(estimators=estimators, final_estimator=LogisticRegression())
clf.fit(X_train, y_train).score(X_test, y_test) # 0.9473684210526315


"""
4. Boosting 思想
- 错题本
- 吾日三省吾身
--------------------------------
AdaBoost 分类器是一种元分类器,它首先在原始数据集上拟合分类器,
然后在同一数据集上拟合分类器的其他副本,但会调整错误分类实例的权重,以便后续分类器更侧重于困难的情况。****
"""
from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import AdaBoostRegressor
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.ensemble import GradientBoostingRegressor


"""
5. 核心集成学习算法
- RandomForestXX
--------------------------------
随机森林是一种元分类器,它在数据集的各个子样本上拟合许多决策树分类器,并使用平均来提高预测准确性和控制过拟合。
森林中的树使用最佳拆分策略,即相当于将 'splitter=“best”' 传递给底层 :class:'~sklearn.tree.DecisionTreeRegressor'。
如果 'bootstrap=True' (默认),则子样本大小由 'max_samples' 参数控制,否则使用整个数据集来构建每棵树。
"""
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import RandomForestRegressor
RandomForestClassifier()

# conda install -y xgboost
from xgboost import XGBClassifier
from xgboost import XGBRegressor

"""
终极推荐:
- 训练速度
- 推理速度
- 处理数据量
- 结果层面
- 综合来看,最优!!!!!!
"""
# conda install -y lightgbm
from lightgbm import LGBMClassifier
from lightgbm import LGBMRegressor

day08-OpenCV、卷积

2024-12-04 15:33:50

复习-深度学习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
核心概念
- 1. deep learning
- 2. layer
- 1. 线性层:linear layer / fully connected layer / dense layer
- inner product / dot product(内积。相乘再相加)
- edge / weight
- output node / bias
- 2. 激活层:activation layer / nonlinearity
- 引入非线性因素,提高模型的表达能力
- ReLU: Rectified Linear Unit
- 3. 模型 model:
- 1. 由layer堆叠构成
- 2. 承继 nn.Module 模块
- 3. class 自定义一个类
- 1. __init__
- 接收和处理超参数
- 实例化后续需要使用的层
- 2. forward
- 接收一个批量的特征
- 根据算法逻辑,调用不同的层来处理数据
- 4. 训练 train
- 1. 作必要的准备(入参):
- 1. model
- 2. optimizer
- 3. loss_fn
- 4. data_loader
- 5. epochs
- 2. 流程(迭代):
- 1. 遍历数据集加载器,取出一批数据
- 2. 把特征 X 导入 模型,做正向传播,得到预测结果 y_pred
- 3. 通过损失函数,计算预测结果和真实结果的误差 loss
- 4. 根据 loss 做反向传播,计算每个参数的偏导数
- 5. 利用优化器让每个参数减去偏导数,执行梯度下降的过程,优化一步
- 6. 利用优化器清空每个参数的偏导数,为下次优化做准备
- 7. 重复上面的过程,直到退出即可
- 5. 过程监控:
- 1. 回归问题:
- get_loss
- 2. 分类问题:
- get_acc
- get_recall
- get_precision
- get_f1_score
- 6. 保存模型
- 1. 保存定义模型的类
- 2. 保存模型训练完之后的权重

- 7. 数据的批量化打包
- 目的:数据量比较大,不可能一次性加载到内存中!所以,需要分批次加载数据!!
- 使用:
- 1. 训练开始
- 2. 从硬盘中读取一批原始数据
- 3. 做必要的预处理和数据增强
- 4. 交给模型训练
- 5. 训练完成之后丢掉
- 6. 从硬盘读取第二批数据
- 7. ...
- 本质:生成器 generator
- PyTorch的策略:
- 第1步:自定义 Dataset,继承 torch.utils.data.Dataset
- 1. __init__ 初始化工作,数据的地址
- 2. __len__ 实现查询数据集中的样本个数
- 3. __getitem__ 实现按索引读取样本
- 第2步:实例化 DataLoader

--------------------------------

表格类数据
- 机器学习
- 全连接网络
- 数据格式:[batch_size, num_features]
- [B, N]

图像/视频类数据?
- 图像:
- 宽度 W :width
- 高度 H :height
- 通道 C :channel

- 格式:
- [batch_size, channel, height, width]
- [N, C, H, W]

- 视频:
- 拆解为一张一张的图像即可
- [batch_size, channel, height, width, time_step]

图片处理 Matplotlib PIL OpenCV

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
"""
1. Matplotlib
- conda install -y matplotlib
- 数据科学三剑客
- 数据可视化
- 高仿Matlab
- plt.imread()
- plt.imshow()
- plt.imsave()
- 没有内置图像处理的功能
- 需要手动去操控NumPy数组
"""
from matplotlib import pyplot as plt
import numpy as np
# 1. 读取图像
img = plt.imread(fname="girl.jpg")
# [H, W, C] RGB
# - (M, N) for grayscale images.
# - (M, N, 3) for RGB images.
# - (M, N, 4) for RGBA images.
img.shape # (246, 164, 3)
# 2. 图像显示
plt.imshow(X=img)
# 处理1:读取 R 层
img_r = img[:, :, 0] # (246, 164)
plt.imshow(X=img_r, cmap="gray")# 灰色显示
# 处理2:只显示图像上半部分
H, W, C = img.shape
plt.imshow(X=img[:H//2, :, :])
# 只显示右侧半张图像
plt.imshow(X=img[:, W//2:, :])
# 处理3:左下角 1/ 4
plt.imshow(X=img[H//2:, :W//2, :])
# 亚采样(4步一走)
plt.imshow(X=img[::4, ::4, :])
# 保存
plt.imsave(fname="girl1.jpg", arr=img[::4, ::4, :])

# --------------------------------

"""
2. PIL: Python Imaging Library
- Python 内置库
- PyTorch 无缝衔接
"""
from PIL import Image
img = Image.open(fp="girl.jpg")
type(img)# PIL.JpegImagePlugin.JpegImageFile

# 'bits', 'close', 'convert', 'copy', 'crop', 'entropy', 'getchannel', 'getcolors', 'getdata', 'getpixel', 'height',
# 'histogram', 'info', 'mode', 'resize', 'rotate', 'save', 'show', 'size', 'split', 'thumbnail', 'tobytes', 'width'
dir(img) # 常用方法

img # 输出图片

# W, H
img.size # (164, 246)
img.width # 164

# 强行改变图像的size(图片显示为 100x100 会变形)
img.resize(size=(100, 100))
# 图像缩小50%
img.resize(size=(int(img.width/2), int(img.height/2)))

# 顺时针 旋转20° 图片大小不变,图片旋转会被截取
img.rotate(angle=-20)

# 灰度图
img.convert(mode="L")

# 获取RGB
img.getpixel(xy=(100, 100))# (243, 126, 91)

np.array(img).shape # (246, 164, 3)

# 逆时针 旋转10° 保存
img.rotate(angle=10).save(fp="girl2.jpg")
# --------------------------------

"""
3. OpenCV
- conda install -y opencv-python
- 平面图像处理金标准!--- OpenCV=二维图片,OpenCL=三维图片
- C++, 使用 Python 调用
"""
# 1. 引入
import cv2
cv2.__version__ # '4.10.0'
# 读取图像
img = cv2.imread(filename="girl.jpg")
# [H, W, C] BGR 模式
img.shape # (246, 164, 3)
img_r = img[:, :, -1]
# 显示图像(最好运行py文件使用,jupyter 运行会卡死===下面我们来再写一个py文件运行来演示)
#cv2.imshow(winname="girl", mat=img)

OpenCV-小案例

图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import cv2

# 读取图像
img = cv2.imread("girl.jpg")

# 显示图像
cv2.imshow(winname="girl", mat=img)

# 按键等待
cv2.waitKey(delay=0) # 无限等待(鼠标放上去,按个按键就退出)
# cv2.waitKey(delay=3000) # 3秒自动退出

# 释放资源
cv2.destroyAllWindows()

摄像头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 引入 OpenCV
import cv2

# 1. 建立连接 0=第0个摄像头
cap = cv2.VideoCapture(0)
print(cap.isOpened()) # True 摄像头已打开

# 2. 增删改查
while True:
# 读取一帧
status, frame = cap.read()
# 读取失败
if not status:
print('error')
break
# 读取成功
# ...添加处理代码

# 图像显示
cv2.imshow(winname="demo", mat=frame)
# 暂留一帧(按ESC键退出)
# 1000 // 24 1秒24帧 27=esc
if cv2.waitKey(delay=1000 // 24) == 27:
break

# 3. 释放资源
cap.release()
cv2.destroyAllWindows()

卷积

0、图片先行

1
2
3
4
5
6
7
8
9
图1:
大走1步,小走一格

图2:
输入3层(卷积核 3*3),输出2层(3*3*2 2表示=2层滤波、2次卷积)
输入padding+1 5*5*3 ===> 7*7*3

图3:
如图2(只不过图3给出了计算)

卷积1
卷积2
卷积3

1、简介

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
卷积操作
- 原始图像
- 卷积核
- 相乘再相加
- 不同的卷积核,可以得到不同的结果,这就是卷积处理的本质!!!

--------------------------------

传统图像处理 VS 人工智能算法
1. 传统图像处理(难学):
- 卷积核是人来设计的!
- 对人的要求是很高的!
- 信号处理、复杂的数学等
- 核的设计是重点、难点、内功的体现!
- 难度大,效果差!
- 自己是一个农民工,自己要考虑如何解决问题!!
- 第1步 ...
- 第2步 ...

2. 人工智能算法(简单):
- 卷积核是参数,是系统优化的目标!!
- 让算法自己来定,需要什么特征就通过反向传播算法改卷积核,从而获得相关的特征即可!
- 甩锅理念! 规则由数据来定,而不是人来定!
- 结果论! 自己是个包工头,自己不管如何实现,只看结果!

2、卷积操作(图片条纹)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import cv2
from matplotlib import pyplot as plt
import numpy as np

img = plt.imread(fname="boy.jpg")
img_r = img[:, :, 0] # (517, 351)
plt.imshow(X=img_r, cmap="gray")# 灰色显示

# 平均滤波(模糊化处理)
N = 10
kernel = np.ones(shape=(N, N)) / N ** 2
img1 = cv2.filter2D(src=img_r, ddepth=-1, kernel=kernel)
plt.imshow(X=img1, cmap="gray")

# 上下相减(横向条纹)
kernel = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]])
img1 = cv2.filter2D(src=img_r, ddepth=-1, kernel=kernel)
plt.imshow(X=img1, cmap="gray")

# 左右相减(纵向条纹)
kernel = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]]).T
img1 = cv2.filter2D(src=img_r, ddepth=-1, kernel=kernel)
plt.imshow(X=img1, cmap="gray")

# 上下左右相减(所有条纹)
kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]]).T
img1 = cv2.filter2D(src=img_r, ddepth=-1, kernel=kernel)
plt.imshow(X=img1, cmap="gray")

3、卷积 计算模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import numpy as np

# 图像
img = np.random.randn(64, 64)

# 卷积核
kernel = np.random.randn(3, 3)

# 初始尺寸
H, W = img.shape # 64
k, k = kernel.shape# 3
# 结果
result = np.zeros(shape=(H-k+1, W-k+1))# (62, 62)

"""
卷积的基本计算
"""
# 遍历 H 方向
for h_idx in range(H-k+1):
# 遍历 W 方向
for w_idx in range(W-k+1):
# 图像块
img_block = img[h_idx: h_idx + k, w_idx: w_idx + k]
# 填充结果
result[h_idx, w_idx] = (img_block * kernel).sum()

pytorch卷积

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import torch
from torch import nn

# 二维卷积:H 和 W 两个方向做卷积
# 进来3层,出去2层
# 3x3 卷积核,步长为2,填充为1
conv2d = nn.Conv2d(in_channels=3, out_channels=2, kernel_size=3, stride=2, padding=1)

# 模拟图像 [N, C, H, W]
X = torch.randn(1, 3, 5, 5)

conv2d(X).shape# torch.Size([1, 2, 3, 3])

conv2d.weight.shape# torch.Size([2, 3, 3, 3])

conv2d.bias.shape# torch.Size([2])

# 批规范化层(Batch Normalization)
bn = nn.BatchNorm2d(num_features=2)

"""
数据
"""
X = torch.randint(low=0, high=101, size=(1, 1, 4, 4),dtype=torch.float32)
# [N, C, H, W]
X.shape# torch.Size([1, 1, 4, 4])
X
# tensor([[[[ 15., 10., 100., 7.],
# [ 45., 42., 37., 3.],
# [ 24., 4., 75., 57.],
# [ 78., 10., 68., 36.]]]])

"""
取最大
"""
# 最大池化、亚采样、把图像变小,丢掉不重要的特征,保留最重要的特征
mp = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)
mp(X) # 拿出最大值
# tensor([[[[ 45., 100.],
# [ 78., 75.]]]])

"""
取平均
"""
avg_pool = nn.AvgPool2d(kernel_size=2, stride=2, padding=0)
avg_pool(X)
# tensor([[[[28.0000, 36.7500],
# [29.0000, 59.0000]]]])

"""
左上角 2*2 取平均
"""
X[:, :, :2, :2]
# tensor([[[[15., 10.],
# [45., 42.]]]])
X[:, :, :2, :2].mean() # tensor(28.)

搭建LeNet5

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import torch
from torch import nn

class Model(nn.Module):
"""
自定义一个神经网络
"""
def __init__(self, in_channels=1, n_classes=10):
"""
初始化
"""
super(Model, self).__init__()

# 1. 特征抽取
self.feature_extractor = nn.Sequential(
nn.Conv2d(in_channels=in_channels,
out_channels=6,
kernel_size=5,
stride=1,
padding=0),
nn.MaxPool2d(kernel_size=2,
stride=2,
padding=0),
nn.Conv2d(in_channels=6,
out_channels=16,
kernel_size=5,
stride=1,
padding=0),
nn.MaxPool2d(kernel_size=2,
stride=2,
padding=0)
)

# 2. 分类输出
self.classifier = nn.Sequential(
nn.Flatten(start_dim=1, end_dim=-1),
nn.Linear(in_features=400, out_features=120),
nn.Linear(in_features=120, out_features=84),
nn.Linear(in_features=84, out_features=n_classes)
)

def forward(self, x):
"""
前向传播
"""
# 1. 先做特征抽取
x = self.feature_extractor(x)
# 2. 再做分类回归
x = self.classifier(x)
return x

model = Model(in_channels=1)

X = torch.randn(2, 1, 32, 32)
y_pred = model(X)
y_pred.shape# torch.Size([2, 10])

day09-手势识别

2024-12-11 07:22:24

手势识别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
"""
1. 原始数据读取
- 并不是把所有图像全部读进内存!
- 而是把所有图像的`路径`和`类别`归纳和梳理出来!
- img_path
- img_label

文件目录
gesture
test
eight
five
...
train
"""
# 尝试读取 train
import os
train_root = os.path.join("gesture", "train")
train_paths = []
train_labels = []

for label in os.listdir(train_root):
label_root = os.path.join(train_root, label)
for file in os.listdir(label_root):
file_path = os.path.join(label_root, file)
train_paths.append(file_path)
train_labels.append(label)

# 尝试读取 test
import os
test_root = os.path.join("gesture", "test")
test_paths = []
test_labels = []

for label in os.listdir(test_root):
label_root = os.path.join(test_root, label)
for file in os.listdir(label_root):
file_path = os.path.join(label_root, file)
test_paths.append(file_path)
test_labels.append(label)

# 构建 标签字典 label dict
labels = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
# {'eight': 8, 'five': 5, 'four': 4, 'nine': 9, 'one': 1, 'seven': 7, 'six': 6, 'three': 3, 'two': 2, 'zero': 0}
label2idx = {label: idx for idx, label in enumerate(labels)}
# {0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six', 7: 'seven', 8: 'eight', 9: 'nine'}
idx2label = {idx: label for label, idx in label2idx.items()}


"""
2. 批量化打包
- 继承 Dataset,自定义一个数据集
- 实例化 DataLoader
"""
# 引入必要的工具类
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
from PIL import Image
from torchvision import transforms
import torch

class GestureDataset(Dataset):
"""
自定义手势识别数据集
"""
def __init__(self, X, y):
"""
初始化
"""
self.X = X
self.y = y

def __getitem__(self, idx):
"""
实现:
- 按下标来索引一个样本
"""
# 获取图像路径
img_path = self.X[idx]
# 读取图像
img = Image.open(fp=img_path)
# 统一大小
img = img.resize((32, 32))
# 转张量 [C, H, W]
# [0, 1]
img = transforms.ToTensor()(img)
# [-1, 1]
img = transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])(img)

# 读取标签
img_label = self.y[idx]
# 标签转 id
img_idx = label2idx.get(img_label)
# 转张量
label = torch.tensor(data=img_idx, dtype=torch.long)

return img, label


def __len__(self):
"""
返回该数据集的样本个数
"""
return len(self.X)

# 训练集加载器
train_dataset = GestureDataset(X=train_paths, y=train_labels)
train_dataloader = DataLoader(dataset=train_dataset, shuffle=True, batch_size=16)
# 测试集加载器
test_dataset = GestureDataset(X=test_paths, y=test_labels)
test_dataloader = DataLoader(dataset=test_dataset, shuffle=False, batch_size=32)

# 测试
for X, y in test_dataloader:
print(X.shape)
print(y.shape)
break
# torch.Size([32, 3, 32, 32])
# torch.Size([32])


"""
3. 搭建模型
"""
import torch
from torch import nn

class Model(nn.Module):
"""
自定义一个神经网络
"""
def __init__(self, in_channels=3, n_classes=10):
"""
初始化
"""
super(Model, self).__init__()

# 1. 特征抽取
self.feature_extractor = nn.Sequential(
nn.Conv2d(in_channels=in_channels,
out_channels=6,
kernel_size=5,
stride=1,
padding=0),
nn.MaxPool2d(kernel_size=2,
stride=2,
padding=0),
nn.Conv2d(in_channels=6,
out_channels=16,
kernel_size=5,
stride=1,
padding=0),
nn.MaxPool2d(kernel_size=2,
stride=2,
padding=0)
)

# 2. 分类输出
self.classifier = nn.Sequential(
nn.Flatten(start_dim=1, end_dim=-1),
nn.Linear(in_features=400, out_features=120),
nn.Linear(in_features=120, out_features=84),
nn.Linear(in_features=84, out_features=n_classes)
)

def forward(self, x):
"""
前向传播
"""
# 1. 先做特征抽取
x = self.feature_extractor(x)
# 2. 再做分类回归
x = self.classifier(x)
return x


"""
4. 训练过程
"""
# 设置训练轮次
epochs = 50
# 设备
device = "cuda" if torch.cuda.is_available() else "cpu"
# 实例化模型
model = Model()
model.to(device=device)
# 优化器
optimizer = torch.optim.Adam(params=model.parameters(), lr=1e-3)
# 损失函数
loss_fn = nn.CrossEntropyLoss()

def train():
for epoch in range(epochs):
for batch_X, batch_y in train_dataloader:
# 1. 数据搬家
batch_X = batch_X.to(device=device)
batch_y = batch_y.to(device=device)
# 2. 正向传播
y_pred = model(batch_X)
# 3. 计算损失
loss = loss_fn(y_pred, batch_y)
# 4. 反向传播
loss.backward()
# 5. 优化一步
optimizer.step()
# 6. 清空梯度
optimizer.zero_grad()
# 7. 打印损失
print(loss.item())

train()
# 0.495683491230011
# 0.2966358959674835
# 0.050676167011260986
# 0.3832390606403351
# 0.1149611622095108
# 0.09388502687215805
# 0.011432101018726826
# 0.08657071739435196
# 0.07936756312847137
# ...
# 1.637368586671073e-05
# 3.1160234357230365e-05
# 4.129716671741335e-06
# 0.00022494769655168056
# 3.035185727640055e-05
# 1.2456989679776598e-05
# 2.30753471441858e-06
# 4.72123283543624e-05
"""
1, 过程监控
2,早停设置(在测试集上,如果连续N=3轮没有性能提升,则停止训练)
3,模型的best.pt和last.pt
4,加载预训练模型 last.pt
5,模型加载、推理流程
6,...

"""

思维导图

2024-12-11

  • 人工智能算法
    人工智能算法
  • KNN算法
    KNN算法
  • 决策树
    决策树
  • 线性回归
    线性回归
  • 逻辑回归
    逻辑回归
  • 集成学习
    集成学习