我有一个包含DNA序列的数据框架,如下所示:
Feature LabelGCTAGATGACAGT 0TTTTAAAACAG 1TAGCTATACT 2 TGGGGCAAAAAAAA 0AATGTCG 3AATGTCG 0AATGTCG 1
其中有一列是DNA序列,另一列是标签,可以是0、1、2、3(即该DNA序列的类别)。我想开发一个神经网络,预测每个序列被分类到1、2或3类别的概率(不考虑0,我对0不感兴趣)。每个序列在数据框架中可以出现多次,并且每个序列可能出现在多个(或所有)类别中。因此,输出应该如下所示:
GCTAGATGACAGT (0.9,0.1,0.2)TTTTAAAACAG (0.7,0.6,0.3)TAGCTATACT (0.3,0.3,0.2) TGGGGCAAAAAAAA (0.1,0.5,0.6)
其中元组中的数字分别表示序列属于类别1、2和3的概率。
这是我的代码:
import numpyfrom keras.datasets import imdbfrom keras.models import Sequentialfrom keras.layers import Densefrom keras.layers import LSTMfrom keras.layers import Dropoutfrom keras.layers.embeddings import Embeddingfrom keras.preprocessing import sequencefrom sklearn.model_selection import StratifiedKFoldfrom keras.callbacks import EarlyStopping, ModelCheckpointimport matplotlibfrom matplotlib import pyplotimport osfrom random import randomfrom numpy import arrayfrom numpy import cumsumimport pandas as pdfrom keras.layers import TimeDistributedfrom keras.layers import Bidirectionalfrom keras.preprocessing.text import Tokenizerfrom sklearn.preprocessing import LabelEncoderos.environ['KMP_DUPLICATE_LIB_OK']='True'%matplotlibfrom sklearn.feature_extraction.text import CountVectorizer# define 10-fold cross validation test harnesskfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=seed)#read in the filedf = pd.read_csv('dna_sequences.txt')X = list(df['dna_sequence'])y = list(df['class'])#convert the sequences to integers for learningtokenizer = Tokenizer(num_words=5,char_level=True)tokenizer.fit_on_texts(X)data_encoded = tokenizer.texts_to_matrix(X,mode='count')kf = kfold.get_n_splits(data_encoded)cvscores = []#for each train, test in cross validation sub-setfor train, test in kfold.split(data_encoded, y): X_train, X_test = data_encoded[train], data_encoded[test] y_train, y_test = data_encoded[train], data_encoded[test] #add layers to model model = Sequential() model.add(Embedding(3000, 32, input_length=5)) model.add(Dropout(0.2)) model.add(Bidirectional(LSTM(20, return_sequences=True), input_shape=(5, 1))) model.add(LSTM(100)) model.add(Dropout(0.2)) model.add(Dense(5, activation='sigmoid')) #compile the model model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) #check the model print(model.summary()) #monitor val accuracy and perform early stopping es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=200) mc = ModelCheckpoint('best_model.h5', monitor='val_accuracy', mode='max', verbose=1, save_best_only=True) #fit the model model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=5, batch_size=64) #change values #evaluate the model scores = model.evaluate(X_test, y_test, verbose=0) print("%s: %.2f%%" % (model.metrics_names[1], scores[1]*100)) cvscores.append(scores[1] * 100)#check the accuracyprint("%.2f%% (+/- %.2f%%)" % (numpy.mean(cvscores), numpy.std(cvscores)))#predict for new set of seqspred_list = ['GTGTGCGCT','GGGGGTCGCTCCCCCC','AAATGTTGT','GTGTGTGGG','CCCCTATATA']#output a probability of sequence being found in each class as described above, and plot accuracy and loss
代码运行正常,并按预期打印了准确率(准确率不是很高,只有62%,但我可以改进,这是我的第一个神经网络,只是想让一个示例运行起来)。
我的问题具体关于预测。能否有人展示一个从拟合模型(如我上面所示)到实际预测的示例。我认为算法包括以下步骤:
- 从交叉验证中找到最佳模型(我尝试通过监控验证准确率部分来实现这一点)
- 用于预测类别的序列列表在pred_list中
- 用训练得到的最佳模型拟合pred_list
- 返回如问题顶部描述的概率。
我知道其他问题中已经存在类似的内容(例如这里):
prediction = model.predict(np.array(tk.texts_to_sequences(text)))print(prediction)
…但是我不知道如何将其与交叉验证结合起来,并且以一种得到我想要的输出方式(即每个序列在训练数据集中被分配到类别1、2或3的三类概率,其中每个序列可以出现在多个类别中)。
编辑1:基于下面的评论,我更改了代码的结尾部分:
(在交叉验证循环中,因此应该缩进)
#evaluate the modelscores = model.evaluate(X_test, y_test, verbose=0)print("%s: %.2f%%" % (model.metrics_names[1], scores[1]*100))cvscores.append(scores[1] * 100)#predict for new set of seqspred_list = ['GTGTGCGCT','GGGGGTCGCTCCCCCC','AAATGTTGT','GTGTGTGGG','CCCCTATATA', 'GGGGGGGGGTTTTTTTT']prediction = model.predict(np.array(tokenizer.texts_to_sequences(pred_list)))predcvscores.append(prediction)
(在交叉验证循环外)
print(predcvscores)#check the accuracyprint("%.2f%% (+/- %.2f%%)" % (numpy.mean(cvscores), numpy.std(cvscores)))
我得到的错误是:
Error when checking input: expected embedding_3_input to have shape (5,) but got array with shape (1,)
我想这是在说我不能直接读取像pred_list这样的序列集?这是否不可能做到,或者我没有采取正确的方法?另外,我不确定这种方法是否会为pred_list中的每个项目提供一个输出,即出现在类别1、2或3的概率,但也许我错了,它确实会这样做。
回答:
你在一个问题中提出了太多且相当不相关的问题,并且其中存在几个问题。我将尝试解决我认为最严重的问题。
首先,如果你有这样的情况:
Feature LabelAATGTCG 3AATGTCG 0AATGTCG 1
即同一个特征可以属于类别0、1或3,没有其他特征存在,那么这里的信息是监督分类可能不适合你当前的问题;要适合,你应该使用额外的特征。
如果你说你只对类别1、2和3感兴趣,并且
不考虑0,我对0不感兴趣
那么在数据准备阶段你应该做的第一件事就是删除数据集中所有类别0的实例;这里不清楚你是否这样做了,即使你这样做了,也不清楚为什么你仍然在讨论中保留类别0。
其次(假设你确实只剩下3个类别),你展示为模型预期输出的内容:
GCTAGATGACAGT (0.9,0.1,0.2)TTTTAAAACAG (0.7,0.6,0.3)TAGCTATACT (0.3,0.3,0.2) TGGGGCAAAAAAAA (0.1,0.5,0.6)
是不正确的;在多类分类中,返回的概率(即这里的括号中的数字)必须正好加起来等于1,这里并非如此。
第三,由于你有一个多类分类问题,你的损失函数应该是categorical_crossentropy
,而不是binary_crossentropy
,后者仅用于二元分类问题。
第四,再次假设你只剩下3个类别,你的模型的最后一层应该是
model.add(Dense(3, activation='softmax') # 这里的单元数应等于类别数)
而你的标签y
应该是一热编码(你可以轻松使用Keras函数to_categorical
来实现)。
第五,仔细查看你循环开始时的数据定义:
X_train, X_test = data_encoded[train], data_encoded[test]y_train, y_test = data_encoded[train], data_encoded[test]
你可以很容易地看到你将特征既作为特征又作为标签传递。我只能猜测这肯定是你的笔误;标签应该是:
y_train, y_test = y[train], y[test]
关于你预测时的错误
Error when checking input: expected embedding_3_input to have shape (5,) but got array with shape (1,)
这是由于你嵌入层中的参数input_length=5
引起的。我在这里承认我对Keras嵌入层完全不熟悉;你可能需要查看文档以确保这个参数和分配的值确实是你认为/打算做的。
除此之外,关于你的具体问题:
我的问题具体关于预测。能否有人展示一个从拟合模型(如我上面所示)到实际预测的示例。
你应该在CV循环外重新编译并重新拟合模型(可能使用在CV期间找到的最佳轮数),使用你全部的数据,然后用它进行预测。
我想现在应该很清楚了,鉴于上述问题,你报告的62%的准确率实际上没有任何意义;无论好坏,Keras不会“保护”你,如果你尝试做一些从建模角度来看没有意义的事情(就像我上面提到的多数事情),比如在多类问题中使用二元交叉熵,或者在回归设置中使用准确率…