這個筆記本顯示瞭如何根據氨基酸序列對蛋白質家族進行分類。 這項工作基於當前自然語言處理(NLP)中深度學習模型的成功,並假設蛋白質序列可以被視為一種語言。 請注意,此任務有一些值得注意的搜索引擎,如BLAST。
在我們深入研究之前,先對數據進行一些預處理:
1. 在structureId上合併
2. 刪除沒有標籤的行
3. 刪除沒有序列的行
4. 選擇蛋白質
import numpy as np import pandas as pd # Merge the two Data set together df = pd.read_csv('../input/pdb_data_no_dups.csv').merge(pd.read_csv('../input/pdb_data_seq.csv'), how='inner', on='structureId') # Drop rows with missing labels df = df[[type(c) == type('') for c in df.classification.values]] df = df[[type(c) == type('') for c in df.sequence.values]] # select proteins df = df[df.macromoleculeType_x == 'Protein'] df.reset_index() df.shape
Preprocessing and visualization of dataset
理想情況下:為了進行比較,我還決定只關注那些實例數大於1000的類(如Akil的這個內核中),它對應於43個最常見的類別。
但是:對於這樣的大數據集4個CPU跑一個小時是不夠的,而只考慮10個最常見的類別。
import matplotlib.pyplot as plt from collections import Counter # count numbers of instances per class cnt = Counter(df.classification) # select only 10 most common classes! top_classes = 10 # sort classes sorted_classes = cnt.most_common()[:top_classes] classes = [c[0] for c in sorted_classes] counts = [c[1] for c in sorted_classes] print("at least " + str(counts[-1]) + " instances per class") # apply to dataframe print(str(df.shape[0]) + " instances before") df = df[[c in classes for c in df.classification]] print(str(df.shape[0]) + " instances after") seqs = df.sequence.values lengths = [len(s) for s in seqs] # visualize fig, axarr = plt.subplots(1,2, figsize=(20,5)) axarr[0].bar(range(len(classes)), counts) plt.sca(axarr[0]) plt.xticks(range(len(classes)), classes, rotation='vertical') axarr[0].set_ylabel('frequency') axarr[1].hist(lengths, bins=100, normed=None) axarr[1].set_xlabel('sequence length') axarr[1].set_ylabel('# sequences') plt.show()
ps. most_common([n])
Return a list of the n most common elements and their counts from the most common to the least. If n is omitted or None, most_common() returns all elements in the counter. Elements with equal counts are ordered arbitrarily:
>>> print(sorted_classes)
[('HYDROLASE', 46336), ('TRANSFERASE', 36424), ('OXIDOREDUCTASE', 34321), ('IMMUNE SYSTEM', 15615), ('LYASE', 11682), ('HYDROLASE/HYDROLASE INHIBITOR', 11218), ('TRANSCRIPTION', 8919), ('VIRAL PROTEIN', 8495), ('TRANSPORT PROTEIN', 8371), ('VIRUS', 6972)]
>>> classes = [c[0] for c in sorted_classes]
>>> print(classes)
['HYDROLASE', 'TRANSFERASE', 'OXIDOREDUCTASE', 'IMMUNE SYSTEM', 'LYASE', 'HYDROLASE/HYDROLASE INHIBITOR', 'TRANSCRIPTION', 'VIRAL PROTEIN', 'TRANSPORT PROTEIN', 'VIRUS']
>>> counts = [c[1] for c in sorted_classes]
>>> print(counts)
[46336, 36424, 34321, 15615, 11682, 11218, 8919, 8495, 8371, 6972]
參考
https://docs.python.org/2/library/collections.html
Transform labels
首先,數據集被簡化為十個最常見類別之一的樣本。 序列的長度範圍從極少數氨基酸到幾千個氨基酸(參見下圖)。
其次,使用sklearn.preprocessing中的LabelBinarizer將字符串中的標籤轉換為one hot表示。
from sklearn.preprocessing import LabelBinarizer # Transform labels to one-hot lb = LabelBinarizer() Y = lb.fit_transform(df.classification)
Further preprocessing of sequences with keras
使用keras函式庫進行文本處理,
Tokenizer:將序列的每個字符轉換為數字
pad_sequences:確保每個序列具有相同的長度(max_length)。 我決定使用最大長度512,這對大多數序列來說應該足夠了。 注意:由於計算時間的原因,減少到256。
train_test_split:來自sklearn將數據分成訓練和測試樣本。
from keras.preprocessing import text, sequence from keras.preprocessing.text import Tokenizer from sklearn.model_selection import train_test_split # maximum length of sequence, everything afterwards is discarded! max_length = 256 #create and fit tokenizer tokenizer = Tokenizer(char_level=True) tokenizer.fit_on_texts(seqs) #represent input data as word rank number sequences X = tokenizer.texts_to_sequences(seqs) X = sequence.pad_sequences(X, maxlen=max_length)
print(X.shape)
print(type(X))
print(type(X[0]))
print(type(X[0][0]))
(188353, 256)
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.int32'>
Build keras model and fit
以前的方法:
hnike的核心依賴於數據的數值特徵(分子量,殘留計數等)90%,僅考慮三種最常見的類別。 在沒有任何一般性聲明的情況下,使用此方案的一些快速測試顯示出優異的結果
abharg的核心使用來自sklearn的CountVectorizer,使用4-gram,然後使用簡單的分類模型,考慮到前43個類別(超過1000個樣本的類別),產生的準確率約為76%。 請注意,此方法也基於序列,並將與我的結果進行比較。
Further improvements
現在這個內核最重要的部分:
1. 最近在成功的NLP中建議使用word embedding操作在keras層(Embedding)。請注意,在我們的例子中只有20個不同的單詞(每個氨基酸),可以嘗試將序列的one hot表示取代word embedding來進行2D卷積,但是這種方法側重於將NLP理論應用於蛋白質序列。
2. 考慮在嵌入序列上使用1D卷積,而不是使用每個n-gram。convolutional kernel的大小可以看作n-gram的大小,過濾器的數量可以看作單字的數量。
3. 為了提高性能,還可以使用深度結構(後續的捲積和池化層),這裡有兩層,其中第一層有64個濾波器,convolutional kernel大小為6,第二層有32個濾波器,大小為3。
4. 平化並傳遞激活函數到完全連接層,其中最後一層是softmax激活,並且大小對應於類別的數量。
使用標準adam優化器的最小化分類交叉熵(categorical crossentropy)來優化此結構。
from keras.models import Sequential from keras.layers import Dense, Conv1D, MaxPooling1D, Flatten from keras.layers import LSTM from keras.layers.embeddings import Embedding embedding_dim = 8 # create the model model = Sequential() model.add(Embedding(len(tokenizer.word_index)+1, embedding_dim, input_length=max_length)) model.add(Conv1D(filters=64, kernel_size=6, padding='same', activation='relu')) model.add(MaxPooling1D(pool_size=2)) model.add(Conv1D(filters=32, kernel_size=3, padding='same', activation='relu')) model.add(MaxPooling1D(pool_size=2)) model.add(Flatten()) model.add(Dense(128, activation='relu')) model.add(Dense(top_classes, activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) print(model.summary())
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=.2) model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=15, batch_size=128)
Evaluate model
評估僅基於準確性(accuracy)和混淆矩陣(confusion matrix)。
from sklearn.metrics import confusion_matrix from sklearn.metrics import accuracy_score from sklearn.metrics import classification_report import itertools train_pred = model.predict(X_train) test_pred = model.predict(X_test) print("train-acc = " + str(accuracy_score(np.argmax(y_train, axis=1), np.argmax(train_pred, axis=1)))) print("test-acc = " + str(accuracy_score(np.argmax(y_test, axis=1), np.argmax(test_pred, axis=1)))) # Compute confusion matrix cm = confusion_matrix(np.argmax(y_test, axis=1), np.argmax(test_pred, axis=1)) # Plot normalized confusion matrix cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis] np.set_printoptions(precision=2) plt.figure(figsize=(10,10)) plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Blues) plt.title('Confusion matrix') plt.colorbar() tick_marks = np.arange(len(lb.classes_)) plt.xticks(tick_marks, lb.classes_, rotation=90) plt.yticks(tick_marks, lb.classes_) #for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])): # plt.text(j, i, format(cm[i, j], '.2f'), horizontalalignment="center", color="white" if cm[i, j] > cm.max() / 2. else "black") plt.ylabel('True label') plt.xlabel('Predicted label') plt.show() print(classification_report(np.argmax(y_test, axis=1), np.argmax(test_pred, axis=1), target_names=lb.classes_))
該結果表明NLP理論適用於蛋白質序列分類。 可以進一步改進(更深的模型,更小的序列,使用n-gram作為word embedding的單字來加速學習),並且應該通過交叉驗證進行驗證。 另一個主要問題是關於overfitting小型訓練數據,但這可以通過諸如Dropout或正規化(regularizing)等著名技術通過增加大權重(kernel_regularizer)或大型激活(activation_regularizer)的懲罰來克服。
參考
https://www.kaggle.com/helmehelmuto/cnn-keras-and-innvestigate
沒有留言:
張貼留言