俗话说,好记性不如烂笔头,看完了黑马的机器学习入门课程后,不禁感叹,立马来归纳整理所学的知识点,方便后续复习。
机器学习入门
人工智能
人工智能概述
我们常常说到人工智能、机器学习、深度学习这些专有名词,那么这些到底有什么差别呢?
大致上可以这样理解:机器学习是人工智能的一个实现途径, 深度学习是机器学习的一个方法发展而来。
人工智能应用
我们学一个东西,总需要知道我们所学习的能做出什么效果吧,这样才能让我们在学习的时候更加有冲劲和动力。
人工智能的应用比较广泛:
- 在预测领域上,比如店铺销量预测、比如广告推荐等。
- 在图像领域上,比如人脸识别、比如无人驾驶(机动车识别交通标志)等。
- 在自然语言处理领域上,比如情感分析、比如翻译、比如文本检测、比如自动聊天(智能客服)等。
结合上述应用,想想我们日常生活中的与之相关的应用,是不是觉得这门学科很有魅力呢,那就快快来学习吧!
机器学习
机器学习基本定义
也许我们在学习某一个知识点的时候,官方的定义会很繁琐和枯燥,但是我们可以尝试着使用自己的理解去给这个知识点下一个简洁的定义。
机器学习可以定义为:从数据中自动分析获得模型,利用模型对未知数据进行预测。
关键字抽取就是:数据+模型+预测。
结合自己的经历可以这样类比:当我们遇到一个难题的时候,这些难题我们可以认为是数据,老师给我们讲解,于是我们会了这一道题怎么做,这个做法我们可以认为是模型,反复练习几次后,老师给你出一个新题目,你尝试着自己去解题,这个新题目可以认为是未知数据,自己解题可以认为是预测。
机器学习算法分类
按照有无目标值,可以分为监督学习和无监督学习。
- 有目标值——监督学习
- 无目标值——无监督学习(其包括聚类 k-means)
在监督学习中,又可以按照目标值是连续还是离散分为:
- 目标值离散——分类问题(其包括k-近邻算法、贝叶斯分类、决策树与随机森林、逻辑回归)
- 目标值连续——回归问题(其包括线性回归、岭回归)
举一个例子来理解:
1、预测明天的气温是多少度? 回归
2、预测明天是阴、晴还是雨? 分类
机器学习开发流程
1)获取数据
- 这一步可以从可用数据集中获取
2)数据处理
- 可以使用numpy、pandas库对数据清洗
- 主要是对缺失值删除或者填充等处理
- 或者是对时间戳的转化或者划分等
- 再就是索引选取符合开发要求的数据等
- 有时还需要从数据中选取特征值x和目标值y进行保存
3)特征工程
- 特征提取
- 特征预处理
- 特征降维
4)机器学习算法训练 - 模型
- 分类问题
- 回归问题
- 无监督学习
5)模型评估
- 评估方法选择
6)应用
- 保存或者加载模型
个人建议
学习建议
大部分的工作其实都在数据处理上,数据处理的好或者不好,有可能直接影响到整个算法模型的优劣,需要十分重视数据处理部分。
其实很多人在刚开始学习机器学习的时候,会有一种强烈的感觉,怎么都是在调包!(其实还要调参)
这里我想说的是:会调包是必须的,只会调包是要完蛋的。
轮子造了就是用来造车的, 但是车好不好, 还是要看造车的人懂不懂轮子。
所以对于人工智能,其实门槛是很高的。
如果一上去,就让你学习一些很深奥的算法,一些数学知识,那可能很快就会丧失学习的热情的。
对于我自己而言,我认为学习,应该是循序渐进的。
首先先是去入门,这一阶段主要是了解学习的这一个方向有哪些知识点,应该有怎么样的学习路线会好一点,整个做出来后的流程和效果是什么样的,然后再去对这些知识点有一个基本的了解和使用,知道在哪里查阅以及遇到问题怎么解决。然后就是去实战,在第一阶段的基础上,尝试着去写一些小型的综合项目,对前期学习的知识点进行巩固和加深,同时对这些的实现可以感同身受,也能更加加深对知识的掌握和对继续学习的冲劲。最后再去深入了解,对于感兴趣的或者重要的知识点,了解其深层次的原理,比如可以去看一些源码,其实这个也是最难的一部分。大概这就是从入门到精通的路线吧(从入门到入土)。
做多了就自然会了,熟能生巧,加油吧!
代码建议
无论是选择pycharm或者anaconda的jupyter开发工具,都要有良好的代码规范。
因为后面学习的算法越来越多,所以对代码进行归类整理就显得格外重要,这样对于后期调试也更加方便。
如果是pycharm,建议先是新建项目,然后对于不同学科进行分包处理,每一个包中对应相应学科的一个个py文件,每一个py文件先把基础的代码框架写起来,然后对于每一个算法案例使用函数封装,对函数命名的时候也要注意意义,能一眼通过函数名就知道这个函数的功能,每次调试都在主函数中调用,并且写好必要的注释说明。
sklearn数据集
学习阶段可以用的数据集:
1)sklearn
2)kaggle
3)UCI
数据集构成: 特征值 + 目标值
以sklearn数据集为例:
API:sklearn.datasets
load_ 获取小规模数据集*
- sklearn.datasets.load_iris()
fetch_ 获取大规模数据集*
- sklearn.datasets.fetch_20newsgroups(data_home=None,subset=‘train’)
数据集的返回值类型为:datasets.base.Bunch(继承自字典)
我们有两种方式去获取其中的字段:
- dict[“key”] = values
- bunch.key = values
我们知道,整个开发流程是先训练模型,然后再去模型评估,所以拿到的数据不是全部都用来训练一个模型,即我们需要进行数据集的划分。
训练数据:用于训练,构建模型。
测试数据:在模型检验时使用,用于评估模型是否有效。
测试集 20%~30%(API默认是25%)
API:sklearn.model_selection.train_test_split(arrays, *options)
划分后得到的为:训练集特征值,测试集特征值,训练集目标值,测试集目标值(注意这个顺序,x为特征值,y为目标值),即得到的依次为: x_train, x_test, y_train, y_test 。
# -*- coding:utf-8 -*-
# @Author : 雾里看花花里看雾(王晓曼)
# @Time : 2022/2/10 16:39
# @FileName: feature_engineering.py
# @Software: PyCharm
# @Blog :https://blog.csdn.net/qq_43779149
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
def datasets_demo():
"""
sklearn数据集使用
:return:
"""
# 获取数据集
iris = load_iris()
print("鸢尾花数据集:\n", iris)
print("查看数据集描述:\n", iris["DESCR"])
print("查看特征值的名字:\n", iris.feature_names)
print("查看特征值:\n", iris.data, iris.data.shape)
# 划分数据集
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=22)
print("训练集的特征值:\n", x_train, x_train.shape)
return None
if __name__ == "__main__":
# 代码1:sklearn数据集使用
datasets_demo()
这里需要简单讲解一下:
# 获取数据集
iris = load_iris()
获取数据集后,其返回的是一个Bunch类型,其继承于字典,iris对象主要字段就是:data(特征值),target(目标值),target_names(目标值名字),feature_names(特征值名字),DESCR(数据集描述),这些均可以通过上述讲解的方式进行获取。
# 划分数据集
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=22)
划分数据集的参数有四个,依次为特征值、目标值、测试集比例、随机种子,其中test_size默认为25%,返回值在刚刚也讲述了,主要是注意顺序以及含义。
特征工程
我们知道,不是所有的信息都能够被计算机进行处理。
业内有一句广为流传的话是:数据和特征决定了机器学习的上限,而模型和算法是在逼近这个上限而已。
特征工程,是指用一系列工程化的方式从原始数据中筛选出更好的数据特征,以提升模型的训练效果。
特征工程基本步骤都是:
1、实例化一个转换器类,其所有的转换器类都继承自transformer类。
2、调用fit_transform(),顾名思义,fit_transform()就是由fit()和transform()组合而成。以标准化为例:其中fit()主要工作是获取特征和目标值有价值的信息,比如求得训练集X的均值,方差,最大值,最小值,这些训练集X固有的属性;transform()就是在fit的基础上,进行标准化,降维,归一化等操作。
特征抽取
API:sklearn.feature_extraction
字典特征提取
把字典中一些类别数据,分别进行转换成特征数据(one-hot)。
在机器学习中为了方便日后的数据处理,我们需要对数据进行抽取,如:{‘name’:‘李华’,‘age’:23}这样一条数据,数据量很小,处理起来很方便,但是当数据量非常大的时候,直接处理就会显得很是耗费处理资源,所以,可以对数据进行特征提取。
API:sklearn.feature_extraction.DictVectorizer(sparse=True,…)
from sklearn.feature_extraction import DictVectorizer
def dict_demo():
"""
字典特征抽取
:return:
"""
data = [{'city': '北京', 'temperature': 100}, {'city': '上海', 'temperature': 60}, {'city': '深圳', 'temperature': 30}]
# 1、实例化一个转换器类
transfer = DictVectorizer(sparse=True)
# 2、调用fit_transform()
data_new = transfer.fit_transform(data)
print("data_new:\n", data_new.toarray(), type(data_new))
print("特征名字:\n", transfer.get_feature_names())
return None
if __name__ == "__main__":
# 代码1:sklearn数据集使用
# datasets_demo()
# 代码2:字典特征抽取
dict_demo()
这里也有一点需要注意的:
# 1、实例化一个转换器类
transfer = DictVectorizer(sparse=True)
# 2、调用fit_transform()
data_new = transfer.fit_transform(data)
这里返回的是一个sparse矩阵,即是稀疏矩阵。如果想查看整个矩阵,可以在得到后使用toarray()函数来进行转换。我们也可以设置sparse=Flase,这样就会返回一个矩阵,但有的特征工程方法没有sparse参数,就只能使用第一种了。
sparse稀疏,将非零值按位置表示出来,节省内存也提高加载效率。
获取特征名可以使用:转换器.get_feature_names()方法。
该方法的应用场景是:
1) 数据集当中类别特征比较多
2)本身拿到的数据就是字典类型
文本特征提取
对于文本,我们一般将单词作为特征词。
但是需要注意,对于英文文章,由于语言本身的特点,所以句子内部就已经通过空格将单词分割开来。但是对于中文文章,句子没有分割单词,所以需要手动分割,否则默认就是一个为一个特征词。但是手动分割又很麻烦,所以我们会借助jieba分词库来自动分词,但是具体的分词还是要自己根据需求设置分词表以及停用表。
**基本步骤就是:
- 准备文本数据(一般也是列表,每一个元素是字符串)
- 英文就直接特征提取,中文就先结巴分词(少量数据可以手动分词)再特征提取
- 特征提取就是上述两步:实例化转换器类、调用fit_transform()**
统计个数:CountVectorizer
API:sklearn.feature_extraction.CountVectorizer
英文:
from sklearn.feature_extraction.text import CountVectorizer
def count_demo():
"""
文本特征抽取:CountVecotrizer
:return:
"""
data = ["life is short,i like like python", "life is too long,i dislike python"]
# 1、实例化一个转换器类
transfer = CountVectorizer(stop_words=["is", "too"])
# 2、调用fit_transform
data_new = transfer.fit_transform(data)
print("data_new:\n", data_new.toarray())
print("特征名字:\n", transfer.get_feature_names())
return None
if __name__ == "__main__":
# 代码1:sklearn数据集使用
# datasets_demo()
# 代码2:字典特征抽取
# dict_demo()
# 代码3:文本特征抽取:CountVecotrizer
count_demo()
中文(手动分词:即仿照英文,加空格):
from sklearn.feature_extraction.text import CountVectorizer
def count_chinese_demo():
"""
中文文本特征抽取:CountVecotrizer
:return:
"""
data = ["我 爱 北京 天安门", "天安门 上 太阳 升"]
# 1、实例化一个转换器类
transfer = CountVectorizer()
# 2、调用fit_transform
data_new = transfer.fit_transform(data)
print("data_new:\n", data_new.toarray())
print("特征名字:\n", transfer.get_feature_names())
return None
if __name__ == "__main__":
# 代码1:sklearn数据集使用
# datasets_demo()
# 代码2:字典特征抽取
# dict_demo()
# 代码3:文本特征抽取:CountVecotrizer
# count_demo()
# 代码4:中文文本特征抽取:CountVecotrizer
count_chinese_demo()
中文(jieba分词:强推 因为一般情况下文本都比较多,手动分词不现实):
import jieba
from sklearn.feature_extraction.text import CountVectorizer
def cut_word(text):
"""
进行中文分词:"我爱北京天安门" --> "我 爱 北京 天安门"
:param text:
:return:
"""
# 这里注意先是jieba.cut()后,要强制转换为list,然后再去转换为str
return " ".join(list(jieba.cut(text)))
def count_chinese_demo2():
"""
中文文本特征抽取,自动分词
:return:
"""
# 将中文文本进行分词
data = ["一种还是一种今天很残酷,明天更残酷,后天很美好,但绝对大部分是死在明天晚上,所以每个人不要放弃今天。",
"我们看到的从很远星系来的光是在几百万年之前发出的,这样当我们看到宇宙时,我们是在看它的过去。",
"如果只用一种方式了解某样事物,你就不会真正了解它。了解事物真正含义的秘密取决于如何将其与我们所了解的事物相联系。"]
data_new = []
# 遍历实现每一句的分词 然后再将处理后的添加到新的列表中
for sent in data:
data_new.append(cut_word(sent))
# print(data_new)
# 1、实例化一个转换器类
transfer = CountVectorizer(stop_words=["一种", "所以"])
# 2、调用fit_transform
data_final = transfer.fit_transform(data_new)
print("data_new:\n", data_final.toarray())
print("特征名字:\n", transfer.get_feature_names())
return None
if __name__ == "__main__":
# 代码1:sklearn数据集使用
# datasets_demo()
# 代码2:字典特征抽取
# dict_demo()
# 代码3:文本特征抽取:CountVecotrizer
# count_demo()
# 代码4:中文文本特征抽取:CountVecotrizer
# count_chinese_demo()
# 代码5:中文文本特征抽取,自动分词
count_chinese_demo2()
这里是作为用法的演示,具体的停用词表还是要设置设置。
统计词频:TfidfVectorizer
API:sklearn.feature_extraction.TfidfVectorizer
在文本处理中,我们经常遇到将一段话变成向量,以组成矩阵来输入到模型中处理。我们这时就可以用到TF-IDF来做,这也是文本特征词的重要程度体现。
TF:词频。TF(w)=(词w在文档中出现的次数)/(文档的总词数)。
IDF:逆向文件频率。有些词可能在文本中频繁出现,但并不重要,也即信息量小,如is,of,that这些单词,这些单词在语料库中出现的频率也非常大,我们就可以利用这点,降低其权重。IDF(w)=log_e((语料库的总文档数)/(语料库中词w出现的文档数))。
将上面的TF-IDF相乘就得到了综合参数:TF-IDF=TF*IDF。
import jieba
from sklearn.feature_extraction.text import CountVectorizer
def tfidf_demo():
"""
用TF-IDF的方法进行文本特征抽取
:return:
"""
# 将中文文本进行分词
data = ["一种还是一种今天很残酷,明天更残酷,后天很美好,但绝对大部分是死在明天晚上,所以每个人不要放弃今天。",
"我们看到的从很远星系来的光是在几百万年之前发出的,这样当我们看到宇宙时,我们是在看它的过去。",
"如果只用一种方式了解某样事物,你就不会真正了解它。了解事物真正含义的秘密取决于如何将其与我们所了解的事物相联系。"]
data_new = []
for sent in data:
data_new.append(cut_word(sent))
# print(data_new)
# 1、实例化一个转换器类
transfer = TfidfVectorizer(stop_words=["一种", "所以"])
# 2、调用fit_transform
data_final = transfer.fit_transform(data_new)
print("data_new:\n", data_final.toarray())
print("特征名字:\n", transfer.get_feature_names())
return None
if __name__ == "__main__":
# 代码1:sklearn数据集使用
# datasets_demo()
# 代码2:字典特征抽取
# dict_demo()
# 代码3:文本特征抽取:CountVecotrizer
# count_demo()
# 代码4:中文文本特征抽取:CountVecotrizer
# count_chinese_demo()
# 代码5:中文文本特征抽取,自动分词
# count_chinese_demo2()
# 代码6:中文分词
# print(cut_word("我爱北京天安门"))
# 代码7:用TF-IDF的方法进行文本特征抽取
tfidf_demo()
有没有发现,上述三种特征提取方法,其实使用方法大致都是一样的,主要区别在于使用的转换器类不一样。当然这里因为入门,所以都是调包,但正是这样看到效果后,再去进一步深入了解,具体的相关参数还是需要进一步调试。
特征预处理
为什么我们要进行归一化/标准化?无量纲化
API:sklearn.preprocessing
比如我们现在有三个特征,一个特征的数值很大,其他特征的数值很小,那么在进行计算的时候,就主要是根据数值很大的那个特征值了,这样肯定不是我们所希望看到的,所以我们需要将这些特征进行无量纲化,这时候就需要使用特征预处理了。
特征预处理主要是归一化和标准化,由于归一化常常会受到异常点的影响,所以标准化一般使用的更多。
归一化
API:sklearn.preprocessing.MinMaxScaler
from sklearn.preprocessing import MinMaxScaler
import pandas as pd
def minmax_demo():
"""
归一化
:return:
"""
# 1、获取数据
data = pd.read_csv("../Data/dating.txt")
# iloc位置索引,其切片操作, 不包含结束
data = data.iloc[:, :3]
print("data:\n", data)
# 2、实例化一个转换器类
# feature_range是指归一化后转换的区间
transfer = MinMaxScaler(feature_range=[2, 3])
# 3、调用fit_transform
data_new = transfer.fit_transform(data)
print("data_new:\n", data_new)
return None
if __name__ == "__main__":
# 代码1:sklearn数据集使用
# datasets_demo()
# 代码2:字典特征抽取
# dict_demo()
# 代码3:文本特征抽取:CountVecotrizer
# count_demo()
# 代码4:中文文本特征抽取:CountVecotrizer
# count_chinese_demo()
# 代码5:中文文本特征抽取,自动分词
# count_chinese_demo2()
# 代码6:中文分词
# print(cut_word("我爱北京天安门"))
# 代码7:用TF-IDF的方法进行文本特征抽取
# tfidf_demo()
# 代码8:归一化
minmax_demo()
标准化
API:sklearn.preprocessing.StandardScaler
from sklearn.preprocessing import MinMaxScaler, StandardScaler
import pandas as pd
def stand_demo():
"""
标准化
:return:
"""
# 1、获取数据
data = pd.read_csv("../Data/dating.txt")
data = data.iloc[:, :3]
print("data:\n", data)
# 2、实例化一个转换器类
transfer = StandardScaler()
# 3、调用fit_transform
data_new = transfer.fit_transform(data)
print("data_new:\n", data_new)
return None
if __name__ == "__main__":
# 代码1:sklearn数据集使用
# datasets_demo()
# 代码2:字典特征抽取
# dict_demo()
# 代码3:文本特征抽取:CountVecotrizer
# count_demo()
# 代码4:中文文本特征抽取:CountVecotrizer
# count_chinese_demo()
# 代码5:中文文本特征抽取,自动分词
# count_chinese_demo2()
# 代码6:中文分词
# print(cut_word("我爱北京天安门"))
# 代码7:用TF-IDF的方法进行文本特征抽取
# tfidf_demo()
# 代码8:归一化
# minmax_demo()
# 代码9:标准化
stand_demo()
特征降维
因为一般数据可能存在很多冗余,所以需要我们进行从这些特征中选取主要特征,这时候就是使用的特征降维了。
降维:降低特征的个数(主要针对二维数组:因为信息一般都是存放在二维数组)
效果:特征与特征之间不相关
特征选择
API:sklearn.feature_selection.VarianceThreshold
API:scipy.stats.pearsonr
-
Filter过滤式
-
方差选择法:低方差特征过滤(因为方差低即是较为集中,故需要过滤掉)
-
相关系数 - 特征与特征之间的相关程度(皮尔逊相关系数 取值范围:–1≤ r ≤+1)
特征与特征之间相关性很高:
1)选取其中一个
2)加权求和
3)主成分分析
-
-
Embeded嵌入式
- 决策树
- 正则化
- 深度学习
from sklearn.feature_selection import VarianceThreshold
from scipy.stats import pearsonr
def variance_demo():
"""
过滤低方差特征
:return:
"""
# 1、获取数据
data = pd.read_csv("../Data/factor_returns.csv")
data = data.iloc[:, 1:-2]
print("data:\n", data)
# 2、实例化一个转换器类
# threshold是用来设置方差过滤临界值
transfer = VarianceThreshold(threshold=10)
# 3、调用fit_transform
data_new = transfer.fit_transform(data)
print("data_new:\n", data_new, data_new.shape)
# 计算某两个变量之间的相关系数
r1 = pearsonr(data["pe_ratio"], data["pb_ratio"])
print("相关系数:\n", r1)
r2 = pearsonr(data['revenue'], data['total_expense'])
print("revenue与total_expense之间的相关性:\n", r2)
return None
if __name__ == "__main__":
# 代码1:sklearn数据集使用
# datasets_demo()
# 代码2:字典特征抽取
# dict_demo()
# 代码3:文本特征抽取:CountVecotrizer
# count_demo()
# 代码4:中文文本特征抽取:CountVecotrizer
# count_chinese_demo()
# 代码5:中文文本特征抽取,自动分词
# count_chinese_demo2()
# 代码6:中文分词
# print(cut_word("我爱北京天安门"))
# 代码7:用TF-IDF的方法进行文本特征抽取
# tfidf_demo()
# 代码8:归一化
# minmax_demo()
# 代码9:标准化
# stand_demo()
# 代码10:低方差特征过滤
variance_demo()
主成分分析
API:sklearn.decomposition.PCA(n_components=None)
n_components:
小数 表示保留百分之多少的信息
整数 减少到多少特征
比如我们在拍一个立体的照片时,比如自拍吧,可能会由于拍照的角度,造成对于原物体的数据损失,那么怎么能够保证在压缩的时候,最大程度的保存原有信息,即根据压缩后的信息仍能识别出原来的是什么呢?这时候就需要使用PCA降维了!
from sklearn.decomposition import PCA
def pca_demo():
"""
PCA降维
:return:
"""
data = [[2,8,4,5], [6,3,0,8], [5,4,9,1]]
# 1、实例化一个转换器类
transfer = PCA(n_components=0.95)
# 2、调用fit_transform
data_new = transfer.fit_transform(data)
print("data_new:\n", data_new)
return None
if __name__ == "__main__":
# 代码1:sklearn数据集使用
# datasets_demo()
# 代码2:字典特征抽取
# dict_demo()
# 代码3:文本特征抽取:CountVecotrizer
# count_demo()
# 代码4:中文文本特征抽取:CountVecotrizer
# count_chinese_demo()
# 代码5:中文文本特征抽取,自动分词
# count_chinese_demo2()
# 代码6:中文分词
# print(cut_word("我爱北京天安门"))
# 代码7:用TF-IDF的方法进行文本特征抽取
# tfidf_demo()
# 代码8:归一化
# minmax_demo()
# 代码9:标准化
# stand_demo()
# 代码10:低方差特征过滤
# variance_demo()
# 代码11:PCA降维
pca_demo()
这样四个特征就变成了两个特征了!但仍然保留了95%信息。
机器学习算法
前面我们学习特征工程的时候知道,所有的特征工程算法都封装在转换器transformer类中,现在正式学习机器学习算法,其实所有的机器学习算法都封装在估计器estimator类中。
估计器的工作流程:
总结一下就是:
1 、实例化一个estimator
2 、estimator.fit(x_train, y_train) 计算
—— 调用完毕,模型生成
3、 模型评估:(不同算法选择的评估方法也可能不一样)
1)直接比对真实值和预测值
y_predict = estimator.predict(x_test)
y_test == y_predict
2)计算准确率
accuracy = estimator.score(x_test, y_test)
从机器学习算法开始,我们就需要结合前面所讲的机器学习开发流程,把所有的步骤都熟练运用上了,这时候综合性就相对有点强了,所以需要格外清楚,每一步究竟在做什么了,注意对于分步骤类似的,也注意区分不要混淆。
分类算法
K近邻算法
KNN核心思想:你的“邻居”来推断出你的类别。
通俗简单的来理解,比如,有三个豌豆,小明、小花、小华。我是小花,现在要判断我的类型。于是分析我的邻居:小明距离我1米,是高杆的;小华距离我5米,是低杆的。当k取1的时候,小明是距离我最近的,于是我被认为是高杆的。
这个k其实是不确定的参数,需要我们自己来设置,但是也会出现一些问题:
- k 值取得过小,容易受到异常点的影响。
- k 值取得过大,样本不均衡的影响。
API:sklearn.neighbors.KNeighborsClassifier(n_neighbors=5,algorithm=‘auto’)
n_neighbors:k值
其实有一个问题,那就是距离是怎么计算的呢?距离公式!
距离公式:
欧氏距离
曼哈顿距离 绝对值距离
明可夫斯基距离
# -*- coding:utf-8 -*-
# @Author : 雾里看花花里看雾(王晓曼)
# @Time : 2022/2/10 22:51
# @FileName: classification_algorithm.py
# @Software: PyCharm
# @Blog :https://blog.csdn.net/qq_43779149
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
def knn_iris():
"""
用KNN算法对鸢尾花进行分类
:return:
"""
# 1)获取数据
iris = load_iris()
# 2)划分数据集
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=22)
# 3)特征工程:标准化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
# 4)KNN算法预估器
estimator = KNeighborsClassifier(n_neighbors=3)
estimator.fit(x_train, y_train)
# 5)模型评估
# 方法1:直接比对真实值和预测值
y_predict = estimator.predict(x_test)
print("y_predict:\n", y_predict)
print("直接比对真实值和预测值:\n", y_test == y_predict)
# 方法2:计算准确率
score = estimator.score(x_test, y_test)
print("准确率为:\n", score)
return None
if __name__ == "__main__":
# 代码1: 用KNN算法对鸢尾花进行分类
knn_iris()
我们可以看到准确率是有97%,还是可以的。
在特征工程这里有一点需要注意的:
# 3)特征工程:标准化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
特征工程这里,我们做的是标准化。特征工程特征工程,是对特征进行处理,目标就不需要处理了,主要是无量纲化。
但是需要注意的是,训练集的特征值做标准化,测试集的特征值也要做标准化,而且要做和训练集一样的标准化。
我们都知道,特征工程所有算法都封装在转换器类中。特征工程一般是两步,先是实例化转换器类,然后调用fit_transform(),现在由于训练集和测试集的特征值都要特征处理,所以在调用函数上需要注意,训练集是fit_transform(),但是测试集的是transform(),因为fit()主要是计算,标准化的话就主要是计算均值、方差,不能使测试集和训练集计算的不一样,要按照训练集计算的结果来转换,一定需要注意!
# 4)KNN算法预估器
estimator = KNeighborsClassifier(n_neighbors=3)
estimator.fit(x_train, y_train)
我们都知道,所有机器学习算法都封装在估计器类中。机器学习算法一般也是两步,先是实例化估计器类,然后调用fit()进行计算,这个和上面的特征工程传递的参数不一样,是训练集的特征值和目标值都传进去。
后面进行模型评估,一般分类算法就是上述所说的两种,即比对真实值和预测值,还有计算精确率。
当然,我们也知道,KNN算法,这个K是不确定的,我们把这种叫做超参数。
对于不确定的参数,我们知道,不同的参数对于结果可能会有影响,那么怎么找到一个最合适的参数,使得我们结果的准确率有一个较好的表现呢?这里就要用到模型选择与调优啦。
交叉验证
API:cv表示几折
超参数与网格搜索
API:sklearn.model_selection.GridSearchCV()
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
def knn_iris_gscv():
"""
用KNN算法对鸢尾花进行分类,添加网格搜索和交叉验证
:return:
"""
# 1)获取数据
iris = load_iris()
# 2)划分数据集
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=22)
# 3)特征工程:标准化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
# 4)KNN算法预估器
estimator = KNeighborsClassifier()
# 加入网格搜索与交叉验证
# 参数准备
param_dict = {"n_neighbors": [1, 3, 5, 7, 9, 11]}
estimator = GridSearchCV(estimator, param_grid=param_dict, cv=10)
estimator.fit(x_train, y_train)
# 5)模型评估
# 方法1:直接比对真实值和预测值
y_predict = estimator.predict(x_test)
print("y_predict:\n", y_predict)
print("直接比对真实值和预测值:\n", y_test == y_predict)
# 方法2:计算准确率
score = estimator.score(x_test, y_test)
print("准确率为:\n", score)
# 最佳参数:best_params_
print("最佳参数:\n", estimator.best_params_)
# 最佳结果:best_score_
print("最佳结果:\n", estimator.best_score_)
# 最佳估计器:best_estimator_
print("最佳估计器:\n", estimator.best_estimator_)
# 交叉验证结果:cv_results_
print("交叉验证结果:\n", estimator.cv_results_)
return None
if __name__ == "__main__":
# 代码1: 用KNN算法对鸢尾花进行分类
# knn_iris()
# 代码2:用KNN算法对鸢尾花进行分类,添加网格搜索和交叉验证
knn_iris_gscv()
这个地方需要注意,模型评估与调优,在哪里加:
# 4)KNN算法预估器
estimator = KNeighborsClassifier()
# 加入网格搜索与交叉验证
# 参数准备
param_dict = {"n_neighbors": [1, 3, 5, 7, 9, 11]}
estimator = GridSearchCV(estimator, param_grid=param_dict, cv=10)
estimator.fit(x_train, y_train)
由于这个网格搜索是需要传入一个估计器,所以需要在实例化估计器后添加,而又需要在计算之前添加,即fit()之前。
由于需要模型选择,所以在实例化KNN的时候就不需要传入K,因为后续评估时候会选择最优的,只需要准备参数即可。
K-近邻总结:
优点:简单,易于理解,易于实现,无需训练
缺点:
1)必须指定K值,K值选择不当则分类精度不能保证
2)懒惰算法,对测试样本分类时的计算量大,内存开销大
使用场景:小数据场景,几千~几万样本,具体场景具体业务去测试
朴素贝叶斯算法
在学习该算法之前,我们需要温习一下概率基础了。
联合概率、条件概率与相互独立:
联合概率:包含多个条件,且所有条件同时成立的概率
P(程序员, 匀称) P(程序员, 超重|喜欢)
P(A, B)
条件概率:就是事件A在另外一个事件B已经发生条件下的发生概率
P(程序员|喜欢) P(程序员, 超重|喜欢)
P(A|B)
相互独立:
P(A, B) = P(A)P(B) <=> 事件A与事件B相互独立
朴素贝叶斯算法,我们怎么来理解它呢?最简单的理解就是, 朴素贝叶斯算法:朴素 + 贝叶斯 ,朴素就是假设特征与特征之间是相互独立,贝叶斯就是贝叶斯公式了。
一般其应用场景为:文本分类,其中单词作为特征。
API:sklearn.naive_bayes.MultinomialNB()
from sklearn.datasets import load_iris, fetch_20newsgroups
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.naive_bayes import MultinomialNB
def nb_news():
"""
用朴素贝叶斯算法对新闻进行分类
:return:
"""
# 1)获取数据
news = fetch_20newsgroups(subset="all")
# 2)划分数据集
x_train, x_test, y_train, y_test = train_test_split(news.data, news.target)
# 3)特征工程:文本特征抽取-tfidf
transfer = TfidfVectorizer()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
# 4)朴素贝叶斯算法预估器流程
estimator = MultinomialNB()
estimator.fit(x_train, y_train)
# 5)模型评估
# 方法1:直接比对真实值和预测值
y_predict = estimator.predict(x_test)
print("y_predict:\n", y_predict)
print("直接比对真实值和预测值:\n", y_test == y_predict)
# 方法2:计算准确率
score = estimator.score(x_test, y_test)
print("准确率为:\n", score)
return None
if __name__ == "__main__":
# 代码1: 用KNN算法对鸢尾花进行分类
# knn_iris()
# 代码2:用KNN算法对鸢尾花进行分类,添加网格搜索和交叉验证
# knn_iris_gscv()
# 代码3:用朴素贝叶斯算法对新闻进行分类
nb_news()
由于我们假设了特征与特征之间相互独立,但是实际上可能不是这样,那如果我们出现算出来的概率为0怎么办呢?那就需要使用拉普拉斯平滑系数了。
拉普拉斯平滑系数
其实默认的拉普拉斯平滑系数就是1.0了。
朴素贝叶斯算法总结:
优点:
对缺失数据不太敏感,算法也比较简单,常用于文本分类。
分类准确度高,速度快(因为做了假设)
缺点:
由于使用了样本属性独立性的假设,所以如果特征属性有关联时其效果不好
决策树算法
可能用下面这个例子来理解决策树:
如何高效的进行决策?特征的先后顺序。
通俗来理解就是,现在特征这么多,我先选择哪一个特征,能够排除的更加多,从而使决策更加高效呢?
决策树的划分依据之一 ------ 信息增益
信息论基础:信息增益=信息熵-条件墒
1)信息
香农:消除随机不定性的东西
小明 年龄 “我今年18岁” - 信息
小华 ”小明明年19岁” - 不是信息
2)信息的衡量 - 信息量 - 信息熵
bit
g(D,A) = H(D) - 条件熵H(D|A)
由于其为分类问题,所以这里的n表示的是有几种情况。
API:sklearn.tree.DecisionTreeClassifier
API(决策树可视化):sklearn.tree.export_graphviz
from sklearn.datasets import load_iris, fetch_20newsgroups
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.tree import DecisionTreeClassifier, export_graphviz
def decision_iris():
"""
用决策树对鸢尾花进行分类
:return:
"""
# 1)获取数据集
iris = load_iris()
# 2)划分数据集
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=22)
# 3)决策树预估器
estimator = DecisionTreeClassifier(criterion="entropy")
estimator.fit(x_train, y_train)
# 4)模型评估
# 方法1:直接比对真实值和预测值
y_predict = estimator.predict(x_test)
print("y_predict:\n", y_predict)
print("直接比对真实值和预测值:\n", y_test == y_predict)
# 方法2:计算准确率
score = estimator.score(x_test, y_test)
print("准确率为:\n", score)
# 可视化决策树
export_graphviz(estimator, out_file="../Result/iris_tree.dot", feature_names=iris.feature_names)
return None
if __name__ == "__main__":
# 代码1: 用KNN算法对鸢尾花进行分类
# knn_iris()
# 代码2:用KNN算法对鸢尾花进行分类,添加网格搜索和交叉验证
# knn_iris_gscv()
# 代码3:用朴素贝叶斯算法对新闻进行分类
# nb_news()
# 代码4:用决策树对鸢尾花进行分类
decision_iris()
可视化部分就是将上述输出的dot文件中的内容复制粘贴到该网站:
得到的如下,具体也可以调参数:
决策树总结:
优点:
可视化 - 可解释能力强
缺点:
容易产生过拟合
随机森林算法
随机森林可以理解为,随机森林=随机+森林。森林,即是包含多个决策树的分类器,随机是包含特征值随机和训练值随机。
随机森林原理过程:
训练集:
N个样本
特征值 目标值
M个特征
随机
两个随机
训练集随机 - N个样本中随机有放回的抽样N个
bootstrap 随机有放回抽样
[1, 2, 3, 4, 5]
新的树的训练集
[2, 2, 3, 1, 5]
特征随机 - 从M个特征中随机抽取m个特征
M >> m
降维
API:sklearn.ensemble.RandomForestClassifier()
具体例子,我将在后续的泰坦尼克号案例中进行讲解。
随机森林总结:
能够有效地运行在大数据集上
处理具有高维特征的输入样本,而且不需要降维
回归算法
线性回归
首先看一下线性模型和线性关系的区别:
线性模型:
-
自变量一次
y = w1x1 + w2x2 + w3x3 + …… + wnxn + b -
参数一次
y = w1x1 + w2x1^2 + w3x1^3 + w4x2^3 + …… + b
线性关系:上述的自变量一次
所以,线性关系一定是线性模型,线性模型不一定是线性关系。
上述我们已经看了线性模型,现在怎么能够找到这样的一个比较好的线性模型呢?
目标:求模型参数,模型参数能够使得预测准确。
现在有一个真实关系,但是我们不知道,所以我们先假定一个关系,然后这两个关系肯定是有一定的误差,那么我们就需要看这个误差,并且进行优化。
真实关系:真实房子价格 = 0.02×中心区域的距离 + 0.04×城市一氧化氮浓度 + (-0.12×自住房平均房价) + 0.254×城镇犯罪率
随意假定:预测房子价格 = 0.25×中心区域的距离 + 0.14×城市一氧化氮浓度 + 0.42×自住房平均房价 + 0.34×城镇犯罪率
已经知道了损失函数:即预测值和真实值的误差,现在就需要优化了!
常用的优化方法就是正规方程和梯度下降(更为常用)。
上述对于分类问题的模型评估是有两种,一是对比真实值和预测值,二是计算精确率。
现在对于回归问题的模型评估,使用的是均方误差。
API:sklearn.metrics.mean_squared_error
正规方程
API:sklearn.linear_model.LinearRegression
# -*- coding:utf-8 -*-
# @Author : 雾里看花花里看雾(王晓曼)
# @Time : 2022/2/11 13:49
# @FileName: regression_algorithm.py
# @Software: PyCharm
# @Blog :https://blog.csdn.net/qq_43779149
from sklearn.datasets import load_boston
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
def linear1():
"""
正规方程的优化方法对波士顿房价进行预测
:return:
"""
# 1)获取数据
boston = load_boston()
# 2)划分数据集
x_train, x_test, y_train, y_test = train_test_split(boston.data, boston.target, random_state=22)
# 3)标准化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
# 4)预估器
estimator = LinearRegression()
estimator.fit(x_train, y_train)
# 5)得出模型
print("正规方程-权重系数为:\n", estimator.coef_)
print("正规方程-偏置为:\n", estimator.intercept_)
# 6)模型评估
y_predict = estimator.predict(x_test)
print("预测房价:\n", y_predict)
error = mean_squared_error(y_test, y_predict)
print("正规方程-均方误差为:\n", error)
return None
if __name__ == "__main__":
# 代码1:正规方程的优化方法对波士顿房价进行预测
linear1()
可以看出,正规方程的均方误差是20.62。
梯度下降
API:sklearn.linear_model.SGDRegressor
from sklearn.datasets import load_boston
from sklearn.linear_model import LinearRegression, SGDRegressor
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
def linear2():
"""
梯度下降的优化方法对波士顿房价进行预测
:return:
"""
# 1)获取数据
boston = load_boston()
print("特征数量:\n", boston.data.shape)
# 2)划分数据集
x_train, x_test, y_train, y_test = train_test_split(boston.data, boston.target, random_state=22)
# 3)标准化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
# 4)预估器
estimator = SGDRegressor(learning_rate="constant", eta0=0.01, max_iter=10000, penalty="l1")
estimator.fit(x_train, y_train)
# 5)得出模型
print("梯度下降-权重系数为:\n", estimator.coef_)
print("梯度下降-偏置为:\n", estimator.intercept_)
# 6)模型评估
y_predict = estimator.predict(x_test)
print("预测房价:\n", y_predict)
error = mean_squared_error(y_test, y_predict)
print("梯度下降-均方误差为:\n", error)
return None
if __name__ == "__main__":
# 代码1:正规方程的优化方法对波士顿房价进行预测
# linear1()
# 代码2:梯度下降的优化方法对波士顿房价进行预测
linear2()
可以看出,梯度下降的均方误差为29.39。
梯度下降优化器扩展:(SAG还挺好的,岭回归中有,可以直接用)
过拟合和欠拟合
欠拟合:学习到数据的特征过少
解决:增加数据的特征数量
过拟合:原始特征过多,存在一些嘈杂特征, 模型过于复杂是因为模型尝试去兼顾各个测试数据点
解决:正则化(L2 更常用)
L1:损失函数 + λ惩罚项 又叫LASSO
L2 :损失函数 + λ惩罚项 又叫Ridge - 岭回归
岭回归
上述已经讲了岭回归的基本概念,现在来看看吧。
岭回归就是带有L2正则化的线性回归-岭回归,其中alpha表示正则化力度=惩罚项系数。
API:sklearn.linear_model.Ridge
import joblib
from sklearn.datasets import load_boston
from sklearn.linear_model import LinearRegression, SGDRegressor, Ridge
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
def linear3():
"""
岭回归对波士顿房价进行预测
:return:
"""
# 1)获取数据
boston = load_boston()
print("特征数量:\n", boston.data.shape)
# 2)划分数据集
x_train, x_test, y_train, y_test = train_test_split(boston.data, boston.target, random_state=22)
# 3)标准化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
# 4)预估器
estimator = Ridge(alpha=0.5, max_iter=10000)
estimator.fit(x_train, y_train)
# 保存模型
# joblib.dump(estimator, "my_ridge.pkl")
# 加载模型
# estimator = joblib.load("my_ridge.pkl")
# 5)得出模型
print("岭回归-权重系数为:\n", estimator.coef_)
print("岭回归-偏置为:\n", estimator.intercept_)
# 6)模型评估
y_predict = estimator.predict(x_test)
print("预测房价:\n", y_predict)
error = mean_squared_error(y_test, y_predict)
print("岭回归-均方误差为:\n", error)
return None
我们可以看出岭回归的均方误差是20.64。
保存模型与加载模型
有时候我们训练出一个很好的模型后,就想下次直接用它,而不是再跑一遍,那就可以使用保存模型和加载模型了。
# 保存模型
# joblib.dump(estimator, "my_ridge.pkl")
# 加载模型
# estimator = joblib.load("my_ridge.pkl")
逻辑回归
逻辑回归,虽然名字叫做回归,但是却常常用于解决分类问题,尤其是二分类问题。
逻辑回归的应用场景:
- 广告点击率 是否会被点击
- 是否为垃圾邮件
- 是否患病
- 是否为金融诈骗
- 是否为虚假账号
- 正例 / 反例
逻辑回归的原理:
线型回归的输出 就是 逻辑回归 的 输入
激活函数
sigmoid函数 [0, 1]
1/(1 + e^(-x))
假设函数/线性模型
1/(1 + e^(-(w1x1 + w2x2 + w3x3 + …… + wnxn + b)))
损失函数
(y_predict - y_true)平方和/总数
逻辑回归的真实值/预测值 是否属于某个类别
对数似然损失
log 2 x
优化损失
梯度下降
逻辑回归评估方法:精确率与召回率,ROC曲线与AUC指标。
分类的评估方法
1 精确率与召回率
1 混淆矩阵
TP = True Possitive
FN = False Negative
2 精确率(Precision)与召回率(Recall)
精确率
召回率 查得全不全
工厂 质量检测 次品 召回率
3 F1-score 模型的稳健型
总共有100个人,如果99个样本癌症,1个样本非癌症 - 样本不均衡
不管怎样我全都预测正例(默认癌症为正例) - 不负责任的模型
准确率:99%
召回率:99/99 = 100%
精确率:99%
F1-score: 2*99%/ 199% = 99.497%
AUC:0.5
TPR = 100%
FPR = 1 / 1 = 100%
2 ROC曲线与AUC指标
1 知道TPR与FPR
TPR = TP / (TP + FN) - 召回率
所有真实类别为1的样本中,预测类别为1的比例
FPR = FP / (FP + TN)
所有真实类别为0的样本中,预测类别为1的比例
具体例子,我将在后续的癌症分类预测-良/恶性乳腺癌肿瘤预测例子中演示。
聚类算法
K-means算法
k-means原理:
k-means评估:
Kmeans性能评估指标
轮廓系数
如果b_i>>a_i:趋近于1效果越好,
b_i<<a_i:趋近于-1,效果不好。
轮廓系数的值是介于 [-1,1] ,
越趋近于1代表内聚度和分离度都相对较优。
具体例子我将在k-means对Instacart Market用户聚类中讲解。
学习小结
至此,机器学习也算是入门了,当然,这也仅仅只是小小的入门,就大体了解了有哪些算法,以及其中所使用的API,但只学习这些还是远远不够的,对于具体算法内部的数学原理以及实现,还需要进一步的学习,继续加油咯!