第三章:第二次AI浪潮(1980s-1990s)
3.1 机器学习的兴起
核心转变:
从"手工编写规则"转向"从数据中学习"。
为什么重要?
- 规则难以手工编写
- 数据更容易获得
- 学习的方法更灵活
3.2 统计学习方法
支持向量机(SVM):
- 1995年:Vladimir Vapnik和Corinna Cortes发表了SVM的经典论文
- 理论基础:基于统计学习理论(Vapnik-Chervonenkis理论)
- 核心思想:找到最优的分类超平面,最大化分类间隔(margin)
核心思想:
找到最优的分类超平面,最大化分类间隔。
实际应用:
- 文本分类
- 图像识别
- 生物信息学
决策树算法:
用树状结构表示决策规则,易于理解和解释。
发展历史:
- 1980s:ID3算法(Iterative Dichotomiser 3)
- 1990s:C4.5算法(ID3的改进版)
- 2000s:随机森林(Random Forest)、梯度提升树(GBDT)等集成方法
示例:
判断是否贷款? ├─ 收入 > 5000? │ ├─ 是 → 批准 │ └─ 否 → 信用记录好? │ ├─ 是 → 批准 │ └─ 否 → 拒绝 └─ 收入 <= 5000 → 拒绝实战代码示例:实现简单的决策树
# ========== 实现简单的决策树 ==========# 决策树是统计学习方法的典型代表# 用树状结构表示决策规则,易于理解和解释classSimpleDecisionTree:""" 简单的决策树 特点: - 用树状结构表示决策规则 - 易于理解和解释 - 可以处理分类和回归任务 """def__init__(self,max_depth=3):""" 初始化决策树 参数: max_depth: 最大深度(防止过拟合) """self.max_depth=max_depth self.tree=Nonedeffit(self,X,y,feature_names=None):""" 训练决策树 参数: X: 特征数据 y: 标签数据 feature_names: 特征名称(可选) """importnumpyasnpiffeature_namesisNone:feature_names=[f'特征{i}'foriinrange(X.shape[1])]self.feature_names=feature_names self.tree=self._build_tree(X,y,feature_names,depth=0)def_build_tree(self,X,y,feature_names,depth):"""递归构建决策树"""importnumpyasnp# 如果达到最大深度或所有样本属于同一类,返回叶子节点ifdepth>=self.max_depthorlen(np.unique(y))==1:# 返回最常见的类别unique,counts=np.unique(y,return_counts=True)return{'type':'leaf','value':unique[np.argmax(counts)]}# 选择最佳特征进行分割best_feature,best_threshold=self._find_best_split(X,y)ifbest_featureisNone:# 无法找到更好的分割,返回叶子节点unique,counts=np.unique(y,return_counts=True)return{'type':'leaf','value':unique[np.argmax(counts)]}# 根据最佳特征和阈值分割数据left_mask=X[:,best_feature]<=best_threshold right_mask=~left_mask# 递归构建左右子树left_tree=self._build_tree(X[left_mask],y[left_mask],feature_names,depth+1)right_tree=self._build_tree(X[right_mask],y[right_mask],feature_names,depth+1)return{'type':'node','feature':best_feature,'feature_name':feature_names[best_feature],'threshold':best_threshold,'left':left_tree,'right':right_tree}def_find_best_split(self,X,y):"""找到最佳的分割特征和阈值"""importnumpyasnp best_gini=float('inf')best_feature=Nonebest_threshold=Noneforfeatureinrange(X.shape[1]):# 尝试不同的阈值thresholds=np.unique(X[:,feature])forthresholdinthresholds:# 计算基尼不纯度left_mask=X[:,feature]<=threshold right_mask=~left_maskifnp.sum(left_mask)==0ornp.sum(right_mask)==0:continuegini=self._calculate_gini(y[left_mask],y[right_mask])ifgini<best_gini:best_gini=gini best_feature=feature best_threshold=thresholdreturnbest_feature,best_thresholddef_calculate_gini(self,y_left,y_right):"""计算基尼不纯度"""importnumpyasnpdefgini_impurity(y):iflen(y)==0:return0unique,counts=np.unique(y,return_counts=True)proportions=counts/len(y)return1-np.sum(proportions**2)n_left=len(y_left)n_right=len(y_right)n_total=n_left+n_rightifn_total==0:return0weighted_gini=(n_left/n_total)*gini_impurity(y_left)+\(n_right/n_total)*gini_impurity(y_right)returnweighted_ginidefpredict(self,X):"""预测"""importnumpyasnp predictions=[]forsampleinX:predictions.append(self._predict_sample(sample,self.tree))returnnp.array(predictions)def_predict_sample(self,sample,node):"""预测单个样本"""ifnode['type']=='leaf':returnnode['value']ifsample[node['feature']]<=node['threshold']:returnself._predict_sample(sample,node['left'])else:returnself._predict_sample(sample,node['right'])defprint_tree(self,node=None,indent=""):"""打印决策树(可视化)"""ifnodeisNone:node=self.treeifnode['type']=='leaf':print(f"{indent}→ 预测:{node['value']}")else:print(f"{indent}{node['feature_name']}<={node['threshold']:.2f}?")print(f"{indent}├─ 是:")self.print_tree(node['left'],indent+" │ ")print(f"{indent}└─ 否:")self.print_tree(node['right'],indent+" ")# ========== 演示:使用决策树进行贷款审批 ==========print("="*60)print("决策树示例:贷款审批系统")print("="*60)importnumpyasnp# 创建示例数据# 特征:收入、信用记录(0=差,1=好)# 标签:0=拒绝,1=批准np.random.seed(42)n_samples=100# 生成数据:收入越高、信用越好,越容易批准incomes=np.random.uniform(3000,10000,n_samples)credit_scores=np.random.choice([0,1],n_samples,p=[0.3,0.7])# 根据规则生成标签labels=[]forincome,creditinzip(incomes,credit_scores):ifincome>5000:labels.append(1)# 批准elifincome>3000andcredit==1:labels.append(1)# 批准else:labels.append(0)# 拒绝X=np.column_stack([incomes,credit_scores])y=np.array(labels)# 训练决策树tree=SimpleDecisionTree(max_depth=3)tree.fit(X,y,feature_names=['收入','信用记录'])# 打印决策树print("\n决策树结构:")tree.print_tree()# 预测新样本print("\n预测新样本:")test_samples=np.array([[6000,1],# 高收入,好信用 -> 应该批准[4000,1],# 中等收入,好信用 -> 应该批准[4000,0],# 中等收入,差信用 -> 应该拒绝[2000,1],# 低收入,好信用 -> 应该拒绝])predictions=tree.predict(test_samples)fori,(sample,pred)inenumerate(zip(test_samples,predictions),1):income,credit=sample credit_str="好"ifcredit==1else"差"result="批准"ifpred==1else"拒绝"print(f" 样本{i}: 收入={income:.0f}, 信用={credit_str}->{result}")# ========== 决策树的优缺点 ==========print("\n"+"="*60)print("决策树的优缺点")print("="*60)print("\n【优点】")print(" - 易于理解和解释:可以清楚地看到决策过程")print(" - 不需要数据预处理:可以处理数值和类别特征")print(" - 可以处理非线性关系")print("\n【缺点】")print(" - 容易过拟合:需要限制深度或使用剪枝")print(" - 对数据变化敏感:小的数据变化可能导致完全不同的树")print(" - 可能产生偏向:倾向于选择具有更多级别的特征")print("\n【现代发展】")print(" - 随机森林:多个决策树的集成,提高准确率")print(" - 梯度提升树(GBDT):逐步改进决策树,提高性能")print(" - XGBoost、LightGBM:高效的梯度提升实现")3.3 神经网络复兴
1986年:反向传播算法
Rumelhart、Hinton和Williams重新发现了反向传播算法,使得训练多层神经网络成为可能。
核心突破:
- 可以训练多层网络
- 解决了单层感知机的局限性
但仍有问题:
- 计算能力不足
- 数据量不够
- 梯度消失问题
实际应用:
- LeNet-5(1998年,Yann LeCun):手写数字识别,成功应用于银行支票识别
- 语音识别:开始使用神经网络
- 但应用范围仍然有限:受限于计算能力和数据量
LeNet的意义:
- 证明了卷积神经网络(CNN)的实用性
- 为后来的深度学习在计算机视觉领域的应用奠定了基础
- 虽然当时没有引起广泛关注,但为2012年的ImageNet突破埋下了伏笔
3.4 第二次AI寒冬(1990s-2000s初)
时间线:
- 1990s中期:专家系统热潮消退
- 1990s后期-2000s初:AI研究资金减少,但机器学习研究仍在继续
原因:
专家系统局限性:
- 知识获取困难(知识工程瓶颈)
- 无法处理不确定性和常识问题
- 维护成本高
神经网络限制:
- 仍然受限于计算能力
- 数据量不足
- 梯度消失问题未完全解决
- 实际应用效果不如预期
资金环境:
- 互联网泡沫破裂(2000-2002年)导致投资减少
- 政府研究资金削减
但这次不同:
- 机器学习理论更加成熟:统计学习理论、支持向量机等
- 计算能力在快速提升:摩尔定律持续,GPU开始用于科学计算
- 数据量在快速增长:互联网普及带来大量数据
- 研究转向:从符号主义转向统计学习,为第三次浪潮奠定基础
历史意义:
这次"寒冬"实际上是一个转型期,AI研究从符号主义转向了数据驱动的机器学习,为后来的深度学习爆发奠定了基础。