KNN分类器执行特征缩放后报ValueError: Unknown label type: 'continuous'
解决KNN特征缩放后出现ValueError的问题
问题背景
为提升KNN模型的准确率,尝试用MinMaxScaler执行特征缩放,但调用model.fit()训练模型时抛出ValueError,未执行特征缩放时模型可正常运行。
原代码
import pandas as pd from sklearn.preprocessing import LabelEncoder import sklearn from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score from sklearn.preprocessing import MinMaxScaler path = "/content/cirrhosis.csv" data = pd.read_csv(path) data = data.loc[0:311] data.head() for col in data.columns: if data[col].dtype == 'int64' or data[col].dtype == 'float64': data[col].fillna(data[col].mean(), inplace=True) elif data[col].dtype == 'object': data[col].fillna(data[col].mode(), inplace=True) label_encoder = LabelEncoder() for column in data.columns: if data[column].dtype == 'object': data[column] = label_encoder.fit_transform(data[column]) print(data) scaler = MinMaxScaler() scaled_data = scaler.fit_transform(data) data = pd.DataFrame(scaled_data, columns=data.columns) inputs = data.drop(['ID', 'Stage'],axis=1) output = data.drop(['ID', 'N_Days', 'Status', 'Drug', 'Age', 'Sex', 'Ascites', 'Hepatomegaly', 'Spiders', 'Edema', 'Bilirubin', 'Cholesterol', 'Albumin', 'Copper', 'Alk_Phos', 'SGOT', 'Tryglicerides', 'Platelets', 'Prothrombin'], axis=1) print(inputs) print(output) x_train, x_test, y_train, y_test = train_test_split(inputs, output, train_size=0.8) model = KNeighborsClassifier(n_neighbors=31) model.fit(x_train,y_train) y_pred = model.predict(x_test)
报错信息
/usr/local/lib/python3.10/dist-packages/sklearn/neighbors/_classification.py:215: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples,), for example using ravel(). return self._fit(X, y) --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-73-f656e2af91bb> in <cell line: 2>() 1 model = KNeighborsClassifier(n_neighbors=31) ----> 2 model.fit(x_train,y_train) 3 y_pred = model.predict(x_test) 4 print(y_pred) 5 print(y_test) 2 frames /usr/local/lib/python3.10/dist-packages/sklearn/utils/multiclass.py in check_classification_targets(y) 216 "multilabel-sequences", 217 ]: ---> 218 raise ValueError("Unknown label type: %r" % y_type) 219 220 ValueError: Unknown label type: 'continuous'
错误原因分析
- 标签被错误缩放:你用
MinMaxScaler对整个数据集(包括分类标签Stage列)做了缩放,导致原本是离散类别的标签变成了0-1之间的连续值。而KNeighborsClassifier是分类模型,要求标签必须是离散的类别,无法识别连续值标签,因此抛出Unknown label type: 'continuous'错误。 - 标签维度问题:
output是DataFrame的单列,作为传入模型的标签是列向量形式,不符合sklearn分类模型要求的一维数组格式,因此触发DataConversionWarning。
修复方案
- 仅对特征数据做缩放:拆分出特征(inputs)和标签(output)后,只对特征部分执行
MinMaxScaler缩放,标签保持原始离散值不变。 - 调整标签维度:将标签转换为一维数组,可通过
ravel()方法实现。
修改后完整代码
import pandas as pd from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score from sklearn.preprocessing import MinMaxScaler path = "/content/cirrhosis.csv" data = pd.read_csv(path) data = data.loc[0:311] # 缺失值填充 for col in data.columns: if data[col].dtype in ['int64', 'float64']: data[col].fillna(data[col].mean(), inplace=True) elif data[col].dtype == 'object': data[col].fillna(data[col].mode()[0], inplace=True) # 修正mode()返回Series的问题 # 类别特征编码 label_encoder = LabelEncoder() for column in data.columns: if data[column].dtype == 'object': data[column] = label_encoder.fit_transform(data[column]) # 提前拆分特征和标签,避免标签被缩放 inputs = data.drop(['ID', 'Stage'], axis=1) output = data['Stage'] # 直接提取单列,更简洁 # 仅对特征数据做缩放 scaler = MinMaxScaler() inputs_scaled = scaler.fit_transform(inputs) inputs = pd.DataFrame(inputs_scaled, columns=inputs.columns) # 划分训练测试集 x_train, x_test, y_train, y_test = train_test_split(inputs, output, train_size=0.8) # 训练模型,将标签转为一维数组 model = KNeighborsClassifier(n_neighbors=31) model.fit(x_train, y_train.ravel()) # 使用ravel()转为一维数组 # 预测并评估 y_pred = model.predict(x_test) print(f"准确率: {accuracy_score(y_test, y_pred)}")
额外说明
- 原代码中
data[col].fillna(data[col].mode(), inplace=True)存在小问题:mode()返回的是Series,直接填充会导致部分缺失值未被正确填充,修改为mode()[0]取第一个众数。 - 提取标签时直接用
data['Stage']比通过drop大量列更简洁且不易出错。
内容的提问来源于stack exchange,提问作者Akshay Basutkar
相关产品推荐
相关产品推荐

