新晋码农一枚小编会定期整理一些写的比较好的代码和知识点作为自己的学习笔记试着做一下批注和补充转载或者参考他人文献会标明出处非商用如有侵权会删改欢迎大家斧正和讨论本章内容较多可点击文章目录进行跳转小编整理和学习了机器学习的相关知识可作为扫盲使用后续也会更新一些技术类的文章大家共同交流学习您的点赞、关注、收藏就是对小编最大的动力系列文章目录机器学习---卷积神经网络【万字长文一文搞定】从 SLP 到 MLP鸢尾花分类实验全解析 —— 神经网络入门必做实验【万字长文一文搞定】基于 ESP32 的冷链物流工业物联网IIoT监控系统全流程实战四种经典降维方法全实验SVD、PCA、LDA、ISOMAP 从原理到代码机器学习---监督学习入门实验全攻略小白友好版目录目录系列文章目录目录前言一、最开头先讲 3 个超级重要概念1. 什么是监督学习2. 什么是欠拟合 / 正确拟合 / 过拟合 欠拟合太简单 正确拟合刚刚好 过拟合背答案3. 标准化是什么二、实验步骤 1准备工作直接复制三、练习 1看数据超级简单✅ 结果 原理小白版四、练习 2决策树从零写 原理超级易懂代码✅ 结果 原理最细版五、练习 3随机森林 原理小白版代码✅ 结果 原理六、练习 4LDF 线性判别 原理超级易懂代码✅ 结果 原理七、练习 5QDF 二次判别 原理代码✅ 结果 原理八、练习 6Parzen 窗 原理小白必看代码九、练习 7KNN 原理代码十、练习 8SVM 原理十一、练习 9线性回归梯度下降 原理代码十二、练习 10SVR 支持向量回归 原理十三、最完整版总结小白秒懂过拟合 / 欠拟合 / 最优模型一句话总结总结前言小编作为新晋码农一枚会定期整理一些写的比较好的代码作为自己的学习笔记会试着做一下批注和补充如转载或者参考他人文献会标明出处非商用如有侵权会删改欢迎大家斧正和讨论大家好这篇是完全小白也能跟着做、完全能看懂的监督学习实验。我会用最简单的话讲原理一步一步带你跑代码、看结果、懂为什么。你会学到什么是分类、什么是回归决策树、随机森林、LDF、QDF、Parzen、KNN、SVM、线性回归、SVR什么叫过拟合、欠拟合、正确拟合每个模型的原理、怎么跑、结果代表什么一、最开头先讲 3 个超级重要概念1. 什么是监督学习你给机器有答案的数据让它学会从输入 → 输出的规律。有标签 监督分类 预测类别是 / 否、0/1/2回归 预测连续数字房价、温度、收入2. 什么是欠拟合 / 正确拟合 / 过拟合 欠拟合太简单模型太笨连训练数据都学不会。训练差、测试差比如树深度 1 正确拟合刚刚好模型学会了规律没背答案。训练好、测试好实验中我们要找的就是它 过拟合背答案模型把噪声都背下来了一到新数据就崩。训练极高、测试暴跌比如树深度 103. 标准化是什么把所有特征缩到差不多大小。距离类模型必须用KNN、Parzen、SVM、回归不用就会崩补充一下涉及到的概念1. 决策树核心分层按条件做判断树形分支决策举例判断水果是不是西瓜第一层表皮纹路清晰是→下一层否→不是西瓜第二层颜色深绿是→判定西瓜否→不是一层层条件筛选顺着分支走完就出结果。特点逻辑直白好理解单一树容易片面、容易过拟合。2. 随机森林核心多棵独立决策树集体投票汇总结果举例全班同学各自单独判断水果品类每个人都是一棵小树各自按自己规则判断最后统计票数票数最多的类别当作最终答案。特点单棵树犯错概率高多树组合大幅降低失误稳定性远高于单决策树。3. LDF 线性判别核心用直线划分数据类别边界举例区分男生女生用身高体重画一条直线直线一侧划为男生另一侧划为女生依靠线性边界分割样本。特点仅适配两类能直线分开的数据结构简单计算快。4. QDF 二次判别核心用曲线划分类别边界举例两类数据交错分布直线切不开改用弧形、波浪曲线划分区域。特点适配复杂交错的数据分类边界拟合能力比线性更强。5. Parzen 窗 帕森窗核心划定局部小区域统计数据密集度判定类别举例把未知样本周围圈出一小块范围数范围内哪类样本数量最多就归属该类。原理依靠数据分布密度分类适合样本分布零散的场景。6. KNN K近邻核心参照距离最近的 K 个邻居从众归类举例新来一名同学找距离他最近 5 位同学这 5 人大多是文科生就判定他为文科生。无需提前训练模型直接比对距离数据量大时运算偏慢。7. SVM 支持向量机核心找出间隔最大的最优分界线举例两类人群站队找一条线把两队彻底分开且这条线距离两边队伍边缘最远。边界容错空间最大抗干扰能力强小样本分类效果出色。8. 线性回归核心拟合直线预测连续数值举例根据房屋面积、楼层画趋势直线顺着直线推算房价根据学习时长预估考试分数。只能拟合线性变化规律波动大的数据预测偏差会变大。9. SVR 支持向量回归核心带误差容忍区间的回归预测举例预估工资时允许结果在合理小范围浮动不用严格贴合拟合曲线。接纳小幅数据偏差异常值、杂讯数据下预测效果比线性回归更稳定。二、实验步骤 1准备工作import numpy as np import matplotlib.pyplot as plt from collections import Counter from sklearn import datasets from sklearn.metrics import accuracy_score, mean_squared_error from sklearn.preprocessing import StandardScaler from sklearn.ensemble import RandomForestClassifier from sklearn.svm import SVC, SVR # 解决中文画图 plt.rcParams[font.sans-serif] [Microsoft YaHei] plt.rcParams[axes.unicode_minus] False # 手动划分训练集测试集 def manual_train_test_split(X, y, test_size0.3, seed42): np.random.seed(seed) indices np.arange(X.shape[0]) np.random.shuffle(indices) test_num int(test_size * len(X)) return X[indices[test_num:]], X[indices[:test_num]], y[indices[test_num:]], y[indices[:test_num]] # 加载分类数据集葡萄酒 wine datasets.load_wine() X, y wine.data, wine.target X_train, X_test, y_train, y_test manual_train_test_split(X, y, test_size0.4, seed1) # 标准化 scaler StandardScaler() X_train_std scaler.fit_transform(X_train) X_test_std scaler.transform(X_test)三、练习 1看数据print(训练集大小, X_train.shape) print(测试集大小, X_test.shape) plt.scatter(X_train[:,0], X_train[:,1], cy_train) plt.xlabel(酒精) plt.ylabel(苹果酸) plt.show()✅ 结果能看到3 种颜色明显分开 原理不同种类酒的化学成分不一样所以机器能轻松分开。特征在空间上分得越开分类越容易。四、练习 2决策树 原理决策树 一连串判断题每次用信息增益选最有用的特征切分。深度太小 → 欠拟合深度太大 → 过拟合深度 3~5 → 最好信息增益越大说明这个特征分的越准。def entropy(y): p np.bincount(y) / len(y) p p[p0] return -np.sum(p * np.log2(p)) def information_gain(y, left, right): return entropy(y) - len(left)/len(y)*entropy(left) - len(right)/len(y)*entropy(right) class Node: def __init__(self,fNone,tNone,lNone,rNone,vNone): self.ff;self.tt;self.ll;self.rr;self.vv def leaf(self): return self.v is not None class DecisionTree: def __init__(self,max_depth5): self.max_depthmax_depth def most(self,y): return Counter(y).most_common(1)[0][0] def best(self,X,y): best_g-1;best_fNone;best_tNone for f in range(X.shape[1]): for t in np.unique(X[:,f]): Ly[X[:,f]t] Ry[X[:,f]t] if len(L)0 or len(R)0:continue ginformation_gain(y,L,R) if gbest_g: best_gg;best_ff;best_tt return best_f,best_t,best_g def grow(self,X,y,d): if dself.max_depth or len(np.unique(y))1: return Node(vself.most(y)) f,t,g self.best(X,y) if g-1:return Node(vself.most(y)) LX[:,f]t return Node(f,t,self.grow(X[L],y[L],d1),self.grow(X[~L],y[~L],d1)) def fit(self,X,y): self.rootself.grow(X,y,0) def pred(self,x,node): if node.leaf():return node.v return self.pred(x,node.l if x[node.f]node.t else node.r) def predict(self,X): return np.array([self.pred(x,self.root) for x in X]) print( 决策树深度实验 ) for d in [1,3,5,10]: modelDecisionTree(max_depthd) model.fit(X_train,y_train) print(f深度{d} 准确率 {accuracy_score(y_test,model.predict(X_test)):.3f})✅ 结果深度1 → 0.681 欠拟合 深度3 → 0.902 最优 深度5 → 0.917 最优 深度10 → 0.889 过拟合 原理深度 1只分一次太简单 → 学不会深度 3~5分配合适 → 学到真实规律深度 10分得太细 → 记住噪声 → 测试变差深度越大模型越复杂越容易把训练集里的 “噪音” 当成规律。五、练习 3随机森林 原理训练很多棵决策树投票决定结果。树越多越稳定太多了不会变好只会变慢为什么随机森林更厉害因为三个随机随机选样本有放回随机选特征多棵树投票抵消单棵树的过拟合print( 随机森林 ) for n in [1,3,5,10,100]: rfRandomForestClassifier(n_estimatorsn,random_state42) rf.fit(X_train,y_train) print(f树{n} 准确率 {rf.score(X_test,y_test):.3f})✅ 结果结果规律树数量 1只有一棵树模型不稳定容易出错 →准确率低树数量 3~10多棵树投票效果明显提升 →准确率快速上升树数量 100准确率提升变缓基本稳定 →性能饱和核心结论随机森林通过集成多棵决策树降低过拟合提升稳定性树太少 → 模型简单欠拟合树适中 →正确拟合树太多 → 效果不再提升只会浪费计算时间 原理单棵树容易过拟合多棵树投票降低过拟合。当树足够多后模型趋于稳定收益明显下降。六、练习 4LDF 线性判别 原理超级易懂LDF 是一种生成模型它假设每类服从正态分布高斯分布所有类别共用同一个协方差矩阵于是用均值和方差直接算出分界线。因为共用协方差所以边界是直线。class LDF: def fit(self,X,y): self.c np.unique(y) self.mu {} self.prior {} cov np.zeros((X.shape[1],X.shape[1])) for cls in self.c: xc X[ycls] self.mu[cls] xc.mean(0) self.prior[cls] len(xc)/len(X) cov np.cov(xc.T)*(len(xc)-1) self.cov cov/(len(X)-len(self.c)) self.inv np.linalg.inv(self.cov) def predict(self,X): res[] for x in X: score[] for cls in self.c: mself.mu[cls] sxself.invm -0.5*mself.invm np.log(self.prior[cls]) score.append(s) res.append(self.c[np.argmax(score)]) return res ldfLDF() ldf.fit(X_train,y_train) print(LDF 准确率, accuracy_score(y_test,ldf.predict(X_test)))✅ 结果接近 0.99 原理数据非常符合高斯分布所以效果爆炸好。当数据满足高斯 同协方差时LDF 接近理论最优。七、练习 5QDF 二次判别 原理QDF 比 LDF 更灵活每个类别自己一个协方差矩阵更灵活但更容易过拟合。因为协方差不同边界是二次曲线能拟合更复杂形状。class QDF: def fit(self,X,y): self.c np.unique(y) self.mu{};self.cov{};self.inv{};self.det{};self.prior{} for cls in self.c: xcX[ycls] self.mu[cls]xc.mean(0) self.cov[cls]np.cov(xc.T) self.inv[cls]np.linalg.inv(self.cov[cls]) self.det[cls]np.linalg.det(self.cov[cls]) self.prior[cls]len(xc)/len(X) def predict(self,X): res[] for x in X: s[] for cls in self.c: dx-self.mu[cls] val-0.5*np.log(self.det[cls]) -0.5*dself.inv[cls]d np.log(self.prior[cls]) s.append(val) res.append(self.c[np.argmax(s)]) return res qdfQDF() qdf.fit(X_train,y_train) print(QDF 准确率, accuracy_score(y_test,qdf.predict(X_test)))✅ 结果 原理协方差矩阵太多 → 参数多 → 小数据集容易过拟合。样本少的时候QDF 估计不可靠。但是LDF 与 QDF 原理真实实验版LDF线性判别函数与 QDF二次判别函数都是基于高斯分布假设的生成式模型。LDF假设所有类别拥有相同的协方差矩阵因此分类边界为线性直线模型简单、稳定。QDF允许每个类别拥有独立的协方差矩阵分类边界为二次曲线模型表达能力更强、更灵活。在本次葡萄酒数据集实验中由于数据类别清晰、分布接近高斯、样本量充足LDF 与 QDF 均表现优秀准确率接近且都处于较高水平。QDF 并未因复杂度提升而出现过拟合反而能更精细地拟合数据分布。八、练习 6Parzen 窗 原理小白必看不假设分布在每个点放一个高斯小铃铛。新来的点哪里铃铛响就是哪一类。铃铛窄h 小→ 只认自己人 → 过拟合铃铛宽h 大→ 一片都一样 → 欠拟合适中最好它是非参数密度估计用样本直接估概率不背公式。class Parzen: def __init__(self,h1): self.hh def fit(self,X,y): self.clsnp.unique(y) self.data{c:X[yc] for c in self.cls} def predict(self,X): res[] for x in X: val[] for c in self.cls: s0 for xi in self.data[c]: snp.exp(-0.5*((x-xi)**2).sum()/(self.h**2)) val.append(s) res.append(self.cls[np.argmax(val)]) return res parzenParzen(h1) parzen.fit(X_train_std,y_train) print(Parzen 准确率, accuracy_score(y_test,parzen.predict(X_test_std)))九、练习 7KNN 原理看最近的 K 个邻居谁多就是谁。K1 → 只看最靠近的 → 过拟合K5 → 综合几个人意见 → 最优K20 → 太 “老好人”全都算 → 欠拟合KNN 是惰性学习不训练预测时现算距离。class KNN: def __init__(self,k5): self.kk def fit(self,X,y): self.XX self.yy def predict(self,X): res[] for x in X: distnp.sqrt(((self.X-x)**2).sum(1)) idxdist.argsort()[:self.k] res.append(Counter(self.y[idx]).most_common(1)[0][0]) return res knnKNN(5) knn.fit(X_train_std,y_train) print(KNN 准确率, accuracy_score(y_test,knn.predict(X_test_std)))十、练习 8SVM 原理找一条最宽间隔的线把两类分开让边界最稳。SVM 的核心思想离得越远越安全只靠 “最边缘的点” 决定边界 → 支持向量RBF 核可以把数据升到高维让原本分不开的数据变可分。svmSVC(kernelrbf,C1,gammascale) svm.fit(X_train_std,y_train) print(SVM 准确率, accuracy_score(y_test,svm.predict(X_test_std)))十一、练习 9线性回归梯度下降 原理拟合一条直线让误差最小。用梯度下降一步步更新沿着下坡走走到谷底。学习率太小走太慢学习率太大走过头合适学习率快速收敛class LR: def __init__(self,lr0.05): self.lrlr def fit(self,X,y): self.wnp.zeros(X.shape[1]) self.b0 for _ in range(50): yhXself.w self.b eyh-y self.w - self.lr * (X.Te)/len(X) self.b - self.lr * e.mean() def predict(self,X): return Xself.w self.b housing datasets.fetch_california_housing() Xr,yrhousing.data,housing.target Xrt,Xtt,yrt,yttmanual_train_test_split(Xr,yr,0.3,50) sStandardScaler() Xrtss.fit_transform(Xrt) Xttss.transform(Xtt) lrLR() lr.fit(Xrts,yrt) print(线性回归 MSE, mean_squared_error(ytt,lr.predict(Xtts)))十二、练习 10SVR 支持向量回归 原理允许一定误差让模型更平滑、更泛化。中间一条带子在带子里面不算错只惩罚外面的点。这样模型不会为了贴合一两个点乱拐弯。svrSVR(kernellinear,C1,epsilon0.1) svr.fit(Xrts,yrt) print(SVR MSE, mean_squared_error(ytt,svr.predict(Xtts)))十三、最完整版总结过拟合 / 欠拟合 / 最优欠拟合太简单 → 训练测试都差最优刚刚好 → 泛化最强过拟合太复杂 → 训练好、测试差模型一句话总结决策树一问一答可解释易过拟合随机森林多树投票稳定抗过拟合LDF/QDF基于高斯分布假设速度快Parzen非参数看密度带宽决定一切KNN看邻居惰性学习必须标准化SVM找最大间隔高维数据很强线性回归拟合直线简单可解释SVR平滑回归抗噪声不钻牛角尖总结以上就是今天要讲的内容本文简单记录了机器学习学习内容仅作为一份简单的笔记使用大家根据注释理解您的点赞关注收藏就是对小编最大的鼓励