我正在训练一个用于分类的模型,具有100个特征,64个标签和150K个观测值(训练集+测试集)。数据已经过预处理且存在噪音。使用SVC我取得了相当不错的结果,足以满足要求。得分为97.8%
SVC模型产生了7700个支持向量,因此实时预测的处理要求很大。
我想知道神经网络是否能有相似的表现。我测试了几种配置,使用1层和2层的网络,但最佳结果是使用1层。我的实验中,使用一个隐藏层包含300个神经元时,得分为94.8%,这不足以满足要求。
数据集已包含在代码中(70MB)。在我的i7 8750H,16GB内存的msi笔记本上,训练时间约为60分钟。
有什么层结构可以改进这个实验?
import numpy as npimport pandas as pdimport timefrom sklearn.svm import SVCfrom sklearn.neural_network import MLPClassifierlocal = 'data.csv'url='https://www.dropbox.com/s/dccj9leppapy9pv/data.csv?dl=1'print('Loading dataset')X = pd.read_csv(url).values[:,1:]train = X[ X[:,-1] == 1][:,:-1]test = X[ X[:,-1] == 0][:,:-1]X_train, y_train = train[:,:-1], train[:,-1]X_test, y_test = test[:,:-1], test[:,-1]now = time.time()model = SVC(C=2500, gamma=10, kernel='rbf',verbose=True)model.fit(X_train, y_train)print('elapsed time:', time.time()-now)score = model.score(X_test,y_test)print('SVC score:', score)print(len(model.support_vectors_) ,'support vectors')now = time.time()model = MLPClassifier(hidden_layer_sizes=(300,), activation='logistic',solver='adam',verbose=True,tol=1e-10,learning_rate_init=0.1,learning_rate='adaptive')model.fit(X_train, y_train)print('elapsed time:', time.time()-now)score = model.score(X_test,y_test)print('NN score:', score)
回答:
你的问题表述有误,但我理解了你的问题所在:
基本上,你有一个大规模、高维度的数据集,具有以下特点:
样本数:150K类别数:64特征数:99
注意 你有99
个特征(不是100个),不包括响应/类别列!
目标:
找到在CPU负载、内存负载和时间方面表现最佳且准确率最高的算法!
已尝试的方法:
SVM
算法给出了极佳的准确率结果,但耗时非常长。
问题:
应调整哪些参数,特别是hidden_layer_sizes
,以实现比你已获得的94.8
更高的准确率,使用hidden_layer_sizes=(300,)
在神经网络
中?
回答
在你的问题中有许多隐含的子问题需要回答。实际上,你需要对你的数据集进行完整的数据分析才能做出好的决策;所以这不是一个简单直接的情况。尽管如此,让我将问题分解成子问题,并尝试解决它们:
首先:
正如我在之前的回答中提到的,有一个选择分类算法的经验法则如下:
- 对于少量特征,使用
逻辑回归
。 - 对于大量特征但数据量不大,使用
SVM
。 - 对于大量特征和大量数据,使用
神经网络
。
对于150K
个观测值和99
个特征,SVM
将花费永远的时间来完成!因此我们剩下逻辑回归
和神经网络
。
根据上述经验法则,神经网络
是一个更好的候选者,实际上它为你提供了94.8%
的准确率,这是一个很好的结果,但是,你需要一个真正出色的分数(>97%)!
其次:
对于人工神经网络
,没有选择隐藏层大小的经验法则,而是有一些指导原则;来自Jeff Heaton的《Java神经网络入门(第二版)》:
隐藏层的数量:
-
0 只能表示线性可分的函数或决策。
-
1 可以近似任何包含从一个有限空间到另一个有限空间的连续映射的函数。
-
2 可以用有理激活函数表示任意决策边界到任意精度,并且可以近似任何平滑映射到任意精度。
另一方面,隐藏层中使用太少的神经元会导致欠拟合,而使用太多神经元则可能导致过拟合,作为经验法则:
-
隐藏层的神经元数量应介于输入层的大小和输出层的大小之间。
-
隐藏层的神经元数量应为输入层大小的2/3加上输出层的大小。
-
隐藏层的神经元数量应小于输入层大小的两倍。
但问题是:如果我们尝试上述建议,会花费多少时间?!
在这里,你需要使用GridSearchCV,你最终会花费比SVM
本身还要多的时间!!!
所以正如你所看到的,准确率和性能之间存在权衡。对于你的特定数据集,你无法同时获得出色的准确率和良好的实时性能,因为它非常大且大部分填充了零!!
该怎么做?
你有以下选项:
1.继续使用你当前的SVM
或ANN
实现,以实现高准确率但最差的实时性能!
2.或者使用Gaussian Naive Bayes,它提供出色的性能(因为它基本上是用于在线分类!),但最差的准确率如下:
import timeimport pandas as pdfrom sklearn.naive_bayes import GaussianNBfrom sklearn.model_selection import StratifiedShuffleSplitimport numpy as npdef getDataset(path, x_attr, y_attr, mapping=None): """ 从CSV文件中提取数据集 :param path: CSV文件的位置 :param x_attr: 特征名称列表 :param y_attr: CSV文件中的Y头名称 :param mapping: 类别整数的字典 :return: 元组,(X, Y) """ df = pd.read_csv(path) if mapping is not None: df.replace(mapping, inplace=True) X = np.array(df[x_attr]).reshape(len(df), len(x_attr)) Y = np.array(df[y_attr]) return X, Ydef run(X_data, Y_data): sss = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=0) train_index, test_index = next(sss.split(X_data, Y_data)) X_train, X_test = X_data[train_index], X_data[test_index] Y_train, Y_test = Y_data[train_index], Y_data[test_index] clf = GaussianNB() print("开始建模...") now = time.time() clf.fit(X_train, Y_train) print('耗时:', time.time() - now) print("建模完成...") print(clf.score(X_test, Y_test))X, Y = getDataset("data.csv", [str(x) for x in range(1,98)] + ["99"], "98")run(X, Y)
结果
开始建模...耗时: 0.12834382057189941建模完成...0.6888808876959838
3.或者选择中间方案,稍微降低准确率期望,然后你可以使用逻辑回归
实现适中的性能,如下所示:
import timeimport pandas as pdfrom sklearn.linear_model import LogisticRegressionfrom sklearn.model_selection import StratifiedShuffleSplitimport numpy as npdef getDataset(path, x_attr, y_attr, mapping=None): """ 从CSV文件中提取数据集 :param path: CSV文件的位置 :param x_attr: 特征名称列表 :param y_attr: CSV文件中的Y头名称 :param mapping: 类别整数的字典 :return: 元组,(X, Y) """ df = pd.read_csv(path) if mapping is not None: df.replace(mapping, inplace=True) X = np.array(df[x_attr]).reshape(len(df), len(x_attr)) Y = np.array(df[y_attr]) return X, Ydef run(X_data, Y_data): sss = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=0) train_index, test_index = next(sss.split(X_data, Y_data)) X_train, X_test = X_data[train_index], X_data[test_index] Y_train, Y_test = Y_data[train_index], Y_data[test_index] clf = LogisticRegression(random_state=0, C=10) print("开始建模...") now = time.time() clf.fit(X_train, Y_train) print('耗时:', time.time() - now) print("建模完成...") print(clf.score(X_test, Y_test))X, Y = getDataset("data.csv", [str(x) for x in range(1,98)] + ["99"], "98")run(X, Y)
结果
开始建模...耗时: 80.22028756141663建模完成...0.9141762953749468
4.或者你可以做的最后一步是检查哪些特征对类别最重要,你可以通过使用森林树
来评估特征的重要性。这里是一个在Scikit-Learn
中如何做的完整且简单的示例,然后创建一个新的数据集版本,但这次只包括最重要的特征。还建议对你当前的数据集进行子采样,删除大部分包含零输入的行/观测值!