day 34 打卡
day 21
常见的降维算法
# 先运行之前预处理好的代码
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
data=pd.read_csv('/Users/gj/东财-学习/python相关资料学习/Python60DaysChallenge-main/data.csv')
# 先筛选字符串变量
discrete_features=data.select_dtypes(include=['object']).columns.to_list()
# Home Ownership 变量
home_ownership_mapping={'Own Home':1,'Rent':2,'Have Mortgage':3,'Home Mortgage':4
}
data['Home Ownership']=data['Home Ownership'].map(home_ownership_mapping)
# Years in current job 变量
years_in_job_mapping={'<1 year':1,'1 year':2,'2 years':3,'3 years':4,'4 years':5,'5 years':6,'6 years':7,'7 years':8,'8 years':9,'9 years':10,'10+ years':11
}
data['Years in current job']=data['Years in current job'].map(years_in_job_mapping)
data=pd.get_dummies(data,columns=['Purpose'])
data2=pd.read_csv('/Users/gj/东财-学习/python相关资料学习/Python60DaysChallenge-main/data.csv')
list_final=[]
for i in data.columns:if i not in data2.columns:list_final.append(i)
for i in list_final:data[i]=data[i].astype(int)# term 0-1 映射
term_mapping={'Short Term':0,'Long Term':1
}
data['Term']=data['Term'].map(term_mapping)
data.rename(columns={'Term':'Long Term'},inplace=True)
continuous_features=data.select_dtypes(include=['int64','float64']).columns.to_list()
# 连续特征用中位数
for feat in continuous_features:mode_value=data[feat].mode()[0]data[feat].fillna(mode_value,inplace=True)# 最开始也说了,很多调参数自带交叉验证,甚至是必选的参数,你如果想要不交叉更麻烦
data.drop(columns=['Id'],inplace=True)
data.info()
特征降维
通常情况下,提到降维,很多时候默认指的是无监督降维,这种方法只需要特征数据本身。但是实际上还包含一种有监督的方法。
1、无监督降维:
定义:这类算法在降维过程中不使用任何关于数据样本的标签信息(比如类别标签、目标值等)。他们仅仅根据数据点本身的分布,方差、相关性、局部结构等特性来寻找低维表示。
输入:只有特征矩阵X
目标:
保留数据中尽可能多的方差(如PCA)
保留数据的局部或全局流型结构(如LLE,LSOMAP,t-SNE,UMAP)
找到能够有效重构原始数据的紧凑表示(如 Autoencoder)
找到统计上独立的成分(如ica)
2、有监督降维:
定义:这类算法在降维过程中利用标签信息来指导降维过程。例如,在分类问题中,我们可能希望将不同类别的样本映射到低维空间,同时保持类别之间的区分度。
输入:特征矩阵X和对应的标签y
目标:
在低维空间中保留类别之间的区分度(如LDA) **典型算法**:* **PCA (Principal Component Analysis)** / **SVD (Singular Value Decomposition)*** t-SNE (t-distributed Stochastic Neighbor Embedding)* UMAP (Uniform Manifold Approximation and Projection)* LLE (Locally Linear Embedding)* Isomap (Isometric Mapping)* Autoencoders (基本形式)* ICA (Independent Component Analysis)* **“只需要特征就可以对特征降维了”**:你这句话描述的就是无监督降维。算法通过分析特征间的关系和分布来进行降维。2、有监督降维:**定义**:这类算法在降维过程中**会利用**数据样本的标签信息(通常是类别标签 `y`)。它们的目标是找到一个低维子空间,在这个子空间中,不同类别的数据点能够被更好地分离开,或者说,这个低维表示更有利于后续的分类(或回归)任务。* **输入**:特征矩阵 `X` **和** 对应的标签向量 `y`。* **目标**:* 最大化不同类别之间的可分性,同时最小化同一类别内部的离散度(如 LDA)。* 找到对预测目标变量 `y` 最有信息量的特征组合。* **典型算法**:* **LDA (Linear Discriminant Analysis)**:这是最经典的监督降维算法。它寻找的投影方向能够最大化类间散度与类内散度之比。* 还有一些其他的,比如 NCA (Neighbourhood Components Analysis),但 LDA 是最主要的代表。* **“还需要有分类标签么”**:是的,对于有监督降维,分类标签(或其他形式的监督信号)是必需的。**举个例子来说明:*** **PCA (无监督)**:如果你有一堆人脸图片,PCA会尝试找到那些能最好地概括所有人脸变化的“主脸”(特征向量),比如脸型、鼻子大小等,它不关心这些人脸属于谁。
* **LDA (有监督)**:如果你有一堆人脸图片,并且你知道每张图片属于哪个人(标签)。LDA会尝试找到那些能最好地区分不同人的人脸特征组合。比如,如果A和B的脸型很像,但眼睛差别很大,LDA可能会更强调眼睛的特征,即使脸型方差更大。PCA是利用最大化方差来实现无监督降维,而LDA则是在此基础上,加入了类别信息,其优化目标就变成了类间差异最大化和类内差异最小化。
PCA等无监督降维方法的目标是保留数据的最大方差,这些方差大的方向不一定是对分类最有用的方向。因此,在分类任务中,LDA通常比PCA更直接有效。
from sklearn.model_selection import train_test_split
X=data.drop(['Credit Default'],axis=1)
y=data['Credit Default']
from sklearn.model_selection import train_test_split
X=data.drop(['Credit Default'],axis=1)
y=data['Credit Default']
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.2,random_state=42)from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.metrics import classification_report, confusion_matrix
import warnings
warnings.filterwarnings("ignore")
# 1.默认参数的随机森林----
# 评估基准模型,这里确实不需要验证集
print("--- 1. 默认参数随机森林 (训练集 -> 测试集) ---")
import time
start_time = time.time() # 记录开始时间
rf_model = RandomForestClassifier(random_state=42)
rf_model.fit(X_train, y_train) # 在训练集上训练
rf_pred = rf_model.predict(X_test) # 在测试集上预测
end_time = time.time() # 记录结束时间print(f"训练与预测耗时: {end_time - start_time:.4f} 秒")
print("\n默认随机森林 在测试集上的分类报告:")
print(classification_report(y_test, rf_pred))
print("默认随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred))# # umap-learn 是一个用于降维和可视化的库,特别适合处理高维数据。它使用了一种基于流形学习的算法,可以有效地将高维数据嵌入到低维空间中,同时保持数据的局部结构。
#!pip install umap-learn -i https://pypi.tuna.tsinghua.edu.cn/simple
# 确保这些库导入,你的原始代码中可能包含了部分
import time
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler # 特征缩放
from sklearn.decomposition import PCA # 主成分分析
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA # 线性判别分析
import umap # 降维
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report,confusion_matrix# 用于评估分类器性能的指标PCA主要适用于那些你认为最重要的信息可以通过数据方差来捕获,并且数据结构主要是线性的情况。
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report,confusion_matrix
import time
import numpy as np
print (f"\n -------2.PCA降维—+随机森林(不使用 Pipeline)")
# 步骤1:特征缩放
scaler = StandardScaler()
X_train_scaled_pca = scaler.fit_transform(X_train)
X_test_scaled_pca= scaler.transform(X_test)# 步骤2:PCA降维
#选择降到10维,或者你可以根据解释方差来选择,例如:
pca_expl=PCA(random_state=42)
pca_expl.fit(X_train_scaled_pca)
cumsum=np.cumsum(pca_expl.explained_variance_ratio_)
n_components_to_keep_95_var=np.argmax(cumsum>=0.95)+1
print(f"需要保留的主成分数量(解释95%方差):{n_components_to_keep_95_var}")
# 我们测试下降到10维的效果
n_components_pca=10
pca_manual=PCA(n_components=n_components_pca,random_state=42)
X_train_pca=pca_manual.fit_transform(X_train_scaled_pca)
X_test_pca=pca_manual.transform(X_test_scaled_pca)
print(f"PCA降维后,训练集形状:{X_train_pca.shape},测试集形状:{X_test_pca.shape}")
start_time_pca_manual=time.time()
# 步骤3:训练随机森林分类器
rf_model_pca=RandomForestClassifier(random_state=42)
rf_model_pca.fit(X_train_pca,y_train)# 步骤4:在测试集上预测
rf_pred_pca_manual=rf_model_pca.predict(X_test_pca)
end_time_pca_manual=time.time()
print(f"手动降维+随机森林分类器耗时:{end_time_pca_manual-start_time_pca_manual:.4f}秒")print("\n手动降维+随机森林分类器 在测试集上的分类报告:")
print(classification_report(y_test, rf_pred_pca_manual))
print("手动降维+随机森林分类器 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_pca_manual))**t-SNE:保持高维数据的局部邻域结构,用于可视化**PCA 的目标是保留数据的全局方差,而 t-SNE 的核心目标是**在高维空间中相似的数据点,在降维后的低维空间中也应该保持相似(即彼此靠近),而不相似的点则应该相距较远。** 它特别擅长于将高维数据集投影到二维或三维空间进行可视化,从而揭示数据中的簇结构或流形结构。---深度学习可视化中很热门**使用 t-SNE 时需要注意的事项:*** **计算成本高**:对于非常大的数据集(例如几十万甚至上百万样本),t-SNE 的计算会非常慢。通常建议在应用 t-SNE 之前,先用 PCA 将数据降到一个适中的维度(例如50维),这样可以显著加速 t-SNE 的计算并可能改善结果。
* **超参数敏感**:* **Perplexity (困惑度)**:这个参数对结果影响较大。常见的取值范围是 5 到 50。较小的困惑度关注非常局部的结构,较大的困惑度则考虑更广泛的邻域。通常需要尝试不同的值。* **n_iter (迭代次数)**:需要足够的迭代次数让算法收敛。默认值通常是1000。如果可视化结果看起来还不稳定,可以尝试增加迭代次数。* **learning_rate (学习率)**:也可能影响收敛。
* **结果的解释**:* **簇的大小和密度在 t-SNE 图中没有直接意义**。t-SNE 会尝试将所有簇展开到相似的密度。不要根据簇在图上的大小来判断原始数据中簇的实际大小或密度。* **点之间的距离在全局上没有意义**。两个相距较远的簇,它们之间的距离并不代表它们在原始高维空间中的实际距离。t-SNE 主要保留的是局部邻域关系。* **多次运行结果可能不同**:由于优化过程的随机初始化和梯度下降的性质,多次运行 t-SNE 可能会得到略微不同的可视化结果。但好的簇结构通常是稳定的。
* **不适合作为通用的有监督学习预处理步骤**:因为它的目标是可视化和保持局部结构,而不是最大化类别可分性或保留全局方差,所以它通常不直接用于提高分类器性能的降维。LDA 或 PCA (在某些情况下) 更适合这个目的。**总结一下**:t-SNE 是一种强大的非线性降维技术,主要用于高维数据的可视化。它通过在低维空间中保持高维空间中数据点之间的局部相似性(邻域关系)来工作。与PCA关注全局方差不同,t-SNE 更关注局部细节。理解它的超参数(尤其是困惑度)和结果的正确解读方式非常重要。from sklearn.manifold import TSNE
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report,confusion_matrix
import time
import numpy as np
import matplotlib.pyplot as plt
import seaborn as snsprint (f"\n---3.t_sen 降维+随机森林")
print("标准t-sen 主要用于可视化,直接用于分类器输入可能效果不佳")
# 步骤1:特征缩放
scaler_tsne=StandardScaler()
X_train_scaled_tsne=scaler_tsne.fit_transform(X_train)
X_test_scaled_tsne=scaler_tsne.transform(X_test)# 步骤 2: t-SNE 降维
# 我们将降维到与PCA相同的维度(例如10维)或者一个适合分类的较低维度。
# t-SNE通常用于2D/3D可视化,但也可以降到更高维度。
# 然而,降到与PCA一样的维度(比如10维)对于t-SNE来说可能不是其优势所在,
# 并且计算成本会显著增加,因为高维t-SNE的优化更困难。
# 为了与PCA的 n_components=10 对比,我们这里也尝试降到10维。
# 但请注意,这可能非常耗时,且效果不一定好。
# 通常如果用t-SNE做分类的预处理(不常见),可能会选择非常低的维度(如2或3)。
n_components_tsne=2
# 对训练集进行fit_transform
tesne_model_train=TSNE(n_components=n_components_tsne,random_state=42,perplexity=30, max_iter=1000,init='pca',learning_rate='auto',n_jobs=-1)print("正在对训练集进行t-SNE降维")
start_time_tsne_rf=time.time()
X_train_tsne=tesne_model_train.fit_transform(X_train_scaled_tsne)
end_tsne_fit_test=time.time()
print(f"t-SNE降维耗时: {end_tsne_fit_test - start_tsne_fit_test:.4f} 秒")
print(f"t-sne降维后,训练集形状: {X_train_tsne.shape}, 测试集形状: {X_test_tsne.shape}")
start_time_tsne_fit_train=time.time()# 步骤3;训练随机森林分类器
rf_model_tsne=RandomForestClassifier(random_state=42)
rf_model_tsne.fit(X_train_tsne,y_train)
# 步骤4:在测试集上预测
rf_pred_tsne_manual=rf_model_tsne.predict(X_test_tsne)
end_time_tsne_rf=time.time()
print(f"t-sne降维后,训练随机森林分类器耗时: {end_time_tsne_fit_train - start_time_tsne_fit_train:.4f} 秒")
total_tsne_time=(end_time_tsne_fit_train-start_time_tsne_fit_train)+(end_tsne_fit_test-start_tsne_fit_test)+(end_time_tsne_rf - start_time_tsne_rf)
print(f"t-sne降维后,总耗时: {total_tsne_time:.4f} 秒")
print ("\n 手动 t-sne+随机森林 在测试集上的分类报告")
print(classification_report(y_test,rf_pred_tsne_manual))
print ("手动 t-sne+随机森林 在测试集上的混淆矩阵")
print(confusion_matrix(y_test,rf_pred_tsne_manual))
**简而言之,LDA 是一种利用类别标签信息来寻找最佳类别分离投影的降维方法,其降维的潜力直接与类别数量挂钩。**
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
import time
import numpy as npimport matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import seaborn as sns
print (f"\n------4. 随机森林分类器 + LDA + 标准化")
# 步骤1:特征缩放
scaler = StandardScaler()
X_train_scaled_lda = scaler.fit_transform(X_train)
X_test_scaled_lda = scaler.transform(X_test)
# 步骤2:LDA降维
n_features=X_train_scaled_lda.shape[1]
if hasattr(y_train,'nunique'):n_classes=y_train.nunique()
elif isinstance(y_train,np.ndarray):n_classes=len(np.unique(y_train))
else:n_classes=len(set(y_train))
max_lda_components=min(n_features,n_classes-1)
# 设置目标降维维度
n_components_lda_target=10if max_lda_components <1:print (f"LDA不适用,因为类别数量为{n_classes}太少,无法产生至少1个判别组件")X_train_lda=X_train_scaled_lda.copy()X_test_lda=X_test_scaled_lda.copy()actual_n_components_lda=n_featuresprint("将使用缩放后的原始特征进行后续操作")
else:actual_n_components_lda=min(n_components_lda_target,max_lda_components)if actual_n_components_lda <1:print(f"计算得到的实际LDA组件数量为{actual_n_components_lda},小于1,LDA不适用") X_train_lda=X_train_scaled_lda.copy()X_test_lda=X_test_scaled_lda.copy()actual_n_components_lda=n_featuresprint("将使用缩放后的原始特征进行后续操作")else:print (f"原始特征数:{n_features} ,类别数:{n_classes}")print(f"LDA最多可将至{max_lda_components}维")print(f"目标降维维度:{n_components_lda_target}")print(f"本次LDA降维将降至{actual_n_components_lda}维") lda=LinearDiscriminantAnalysis(n_components=actual_n_components_lda)X_train_lda=lda.fit_transform(X_train_scaled_lda,y_train)X_test_lda=lda.transform(X_test_scaled_lda)
print (f"LDA降维后的特征数:{X_train_lda.shape},测试集特征数:{X_test_lda.shape}")
start_time_lda_rf=time.time()
# 步骤3:训练随机森林分类器
rf_model_lda=RandomForestClassifier(random_state=42)
rf_model_lda.fit(X_train_lda,y_train)
#步骤4:在测试集上预测
rf_pred_lda_manual=rf_model_lda.predict(X_test_lda)
end_time_lda_rf=time.time()
print (f"LDA降维数据上,随机森林分类器训练与预测耗时:{end_time_lda_rf-start_time_lda_rf:.4f} 秒")print("\n 手动lda+随机森林在测试集上的分类报告:")
print(classification_report(y_test, rf_pred_lda_manual))
print("手动lda+随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_lda_manual))
@浙大疏锦行