前言

不多说了,今天争取写两篇,完整代码见 https://github.com/zong4/AILearning。

Supervised Learning

先来讲讲监督学习。

监督学习最大的特点就是有数据标签,会有人告诉 AI 那个东西是什么,这样的话准确性肯定会高一些。

最常见的应用的话就是 Classification(分类),我们也来试一下,基本就是一通百通。

预测用户是否购买

在这个例子中我们将根据给出的数据来预测用户在网上购物时是否会购买商品,从而判断是否给它推广告。

主函数

先来看看主函数。

  1. 首先需要加载数据,并分成训练集和预测集。
  2. 然后的话就开始训练模型。
  3. 再让模型去预测集上跑一遍输出预测结果。
  4. 最后来算一算模型的 Sensitivity(真阳率)和 Specificity(真阴率)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def main():

# Load data from spreadsheet and split into train and test sets
evidence, labels = load_data("./shopping/shopping.csv")
X_train, X_test, y_train, y_test = train_test_split(
evidence, labels, test_size=TEST_SIZE # TEST_SIZE = 0.4
)

# Train model and make predictions
model = train_model(X_train, y_train)
predictions = model.predict(X_test)
sensitivity, specificity = evaluate(y_test, predictions)

# Print results
print(f"Correct: {(y_test == predictions).sum()}")
print(f"Incorrect: {(y_test != predictions).sum()}")
print(f"True Positive Rate: {100 * sensitivity:.2f}%")
print(f"True Negative Rate: {100 * specificity:.2f}%")

加载数据

数据类型还挺多的,正好给大家看看 Python 怎么加载 csv。

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
def load_data(filename):
with open(filename) as csvfile:
reader = csv.DictReader(csvfile)

evidence = []
labels = []

for row in reader:
evidence.append([
int(row["Administrative"]),
float(row["Administrative_Duration"]),
int(row["Informational"]),
float(row["Informational_Duration"]),
int(row["ProductRelated"]),
float(row["ProductRelated_Duration"]),
float(row["BounceRates"]),
float(row["ExitRates"]),
float(row["PageValues"]),
float(row["SpecialDay"]),
int(datetime.strptime(row["Month"], '%b').month - 1) if row["Month"] != "June" else 5,
int(row["OperatingSystems"]),
int(row["Browser"]),
int(row["Region"]),
int(row["TrafficType"]),
0 if row["VisitorType"] == "New_Visitor" else 1,
0 if row["Weekend"] == "FALSE" else 1
])

labels.append(1 if row["Revenue"] == "TRUE" else 0)

return evidence, labels

训练模型

我这里写的很简单,基本都用的默认参数,给大家稍微讲一下。

首先是这个K近邻算法,就是对于每一个待分类的样本,找到离它最近的 n_neighbors 个邻居,它们的众数就是算法的预测结果。

这里默认的距离计算方式是欧拉距离。

1
2
3
4
5
def train_model(evidence, labels):
model = KNeighborsClassifier(n_neighbors=1)
model.fit(evidence, labels)

return model

评估模型

最后就是评估模型了。

1
2
3
4
5
def evaluate(labels, predictions):
sensitivity = sum([1 for i in range(len(labels)) if labels[i] == 1 and predictions[i] == 1]) / sum([1 for label in labels if label == 1])
specificity = sum([1 for i in range(len(labels)) if labels[i] == 0 and predictions[i] == 0]) / sum([1 for label in labels if label == 0])

return sensitivity, specificity

结果

给大家看看结果,咋一看貌似还行,毕竟真阴率有90%,但其实很垃圾,因为真阳率才是重要的。

真阳率低代表了客人想买但是模型觉得他们不买,那不完蛋了吗,销售额还要不要了。

1
2
3
4
Correct: 4072
Incorrect: 860
True Positive Rate: 39.50%
True Negative Rate: 90.84%

优化

数据标准化

这一步基本是必要的,毕竟是要算距离,不标准化数据的话,结果就会被一些特征过度影响,导致都偏向一边,就像现在偏向给出 False 一样。

1
2
3
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

给大家再看看结果,是不是正确率还是有明显的提升的。

1
2
3
4
Correct: 4153
Incorrect: 779
True Positive Rate: 44.03%
True Negative Rate: 91.93%

筛选特征

那我们还可以剔除一些不必要的特征来减少噪声,这里我尝试选了10个最重要的特征。

1
2
3
selector = SelectKBest(score_func=f_classif, k=10)
X_train_selected = selector.fit_transform(X_train, y_train)
X_test_selected = selector.transform(X_test)

结果看上去又好了一些。

1
2
3
4
Correct: 4197
Incorrect: 735
True Positive Rate: 51.07%
True Negative Rate: 91.64%

近邻数量

那我这边又试着改了一下 n_neighbors,毕竟 n_neighbors=1 就代表最近的是啥我们就预测为啥,这肯定不行啊,所以我改成了5。

可以看到结果上真阳率略有降低,真阴率倒是提高了。

1
2
3
4
Correct: 4329
Incorrect: 603
True Positive Rate: 48.43%
True Negative Rate: 95.33%

这说明什么?说明标签为 False 的数据很分散,所以我们可以再减少选取的特征,或者可以进行特征组合

这边我尝试将特征减到了6个,基本已经不能再减了,结果比之前好了很多,说明原始数据噪声太多了。

1
2
3
4
Correct: 4362
Incorrect: 570
True Positive Rate: 53.08%
True Negative Rate: 95.24%

近邻权重

OK,最后我又试着调整了一下近邻权重。

1
2
3
4
5
6
7
8
# 自定义权重函数
def custom_weight(distances):
"""
自定义权重函数,这里简单地将距离的倒数作为权重
"""
return 1.0 / distances

model = KNeighborsClassifier(n_neighbors=5, metric="euclidean", weights=custom_weight)

结果也基本大差不差。

1
2
3
4
Correct: 4326
Incorrect: 606
True Positive Rate: 53.33%
True Negative Rate: 94.32%

修改模型

这边又试着换成了决策树模型,基本也差不多,就不给看结果了,大家可以自己试试,然后改改参数说不定会更好。

其他

当然除了上面这些我们还有很多事情可以做,比如剔除离群值,修改评估函数,但这都是数据挖掘的知识了,大家可以自行了解。

Semi-supervised Learning

最后来提一嘴半监督学习,这主要是因为标注成本太高了,麻烦还不一定准,不如干脆让 AI 自己学。

具体的方法有自训练法,协同训练法,这些都以后再讲了。

后记

那这样的话机器学习的分类也讲完了,下一篇就是强化学习了,今天争取赶出来。