Package: sgt
Sequence Graph Transform (SGT) — Sequence Embedding for Clustering, Classification, and SearchMaintained by: Chitta RanjanEmail:cran2367@gmail.com| LinkedIn:https://www.linkedin.com/in/chitta-ranjan-b0851911/The following will cover,SGT Class DefinitionInstallationTest ExamplesSequence Clustering ExampleSequence Classification ExampleSequence Search ExampleSGT - Spark for Distributed ComputingSGT Class DefinitionSequence Graph Transform (SGT) is a sequence embedding function. SGT extracts the short- and long-term sequence features and embeds them in a finite-dimensional feature space. The long and short term patterns embedded in SGT can be tuned without any increase in the computation."class SGT():
    '''
    Compute embedding of a single or a collection of discrete item 
    sequences. A discrete item sequence is a sequence made from a set
    discrete elements, also known as alphabet set. For example,
    suppose the alphabet set is the set of roman letters, 
    {A, B, ..., Z}. This set is made of discrete elements. Examples of
    sequences from such a set are AABADDSA, UADSFJPFFFOIHOUGD, etc.
    Such sequence datasets are commonly found in online industry,
    for example, item purchase history, where the alphabet set is
    the set of all product items. Sequence datasets are abundant in
    bioinformatics as protein sequences.
    Using the embeddings created here, classification and clustering
    models can be built for sequence datasets.
    Read more in https://arxiv.org/pdf/1608.03533.pdf
    '''

    Parameters
    ----------
    Input:

    alphabets       Optional, except if mode is Spark. 
                    The set of alphabets that make up all 
                    the sequences in the dataset. If not passed, the
                    alphabet set is automatically computed as the 
                    unique set of elements that make all the sequences.
                    A list or 1d-array of the set of elements that make up the      
                    sequences. For example, np.array(["A", "B", "C"].
                    If mode is 'spark', the alphabets are necessary.

    kappa           Tuning parameter, kappa > 0, to change the extraction of 
                    long-term dependency. Higher the value the lesser
                    the long-term dependency captured in the embedding.
                    Typical values for kappa are 1, 5, 10.

    lengthsensitive Default false. This is set to true if the embedding of
                    should have the information of the length of the sequence.
                    If set to false then the embedding of two sequences with
                    similar pattern but different lengths will be the same.
                    lengthsensitive = false is similar to length-normalization.

    flatten         Default True. If True the SGT embedding is flattened and returned as
                    a vector. Otherwise, it is returned as a matrix with the row and col
                    names same as the alphabets. The matrix form is used for            
                    interpretation purposes. Especially, to understand how the alphabets
                    are "related". Otherwise, for applying machine learning or deep
                    learning algorithms, the embedding vectors are required.

    mode            Choices in {'default', 'multiprocessing'}. Note: 'multiprocessing' 
                    mode requires pandas==1.0.3+ and pandarallel libraries.

    processors      Used if mode is 'multiprocessing'. By default, the 
                    number of processors used in multiprocessing is
                    number of available - 1.
    '''


    Attributes
    ----------
    def fit(sequence)

    Extract Sequence Graph Transform features using Algorithm-2 in https://arxiv.org/abs/1608.03533.
    Input:
    sequence        An array of discrete elements. For example,
                    np.array(["B","B","A","C","A","C","A","A","B","A"].

    Output: 
    sgt embedding   sgt matrix or vector (depending on Flatten==False or True) of the sequence


    --
    def fit_transform(corpus)

    Extract SGT embeddings for all sequences in a corpus. It finds
    the alphabets encompassing all the sequences in the corpus, if not inputted. 
    However, if the mode is 'spark', then the alphabets list has to be
    explicitly given in Sgt object declaration.

    Input:
    corpus          A list of sequences. Each sequence is a list of alphabets.

    Output:
    sgt embedding of all sequences in the corpus.


    --
    def transform(corpus)

    Find SGT embeddings of a new data sample belonging to the same population
    of the corpus that was fitted initially.Install SGTInstall SGT in Python by running,$ pip install sgtimportsgtsgt.__version__fromsgtimportSGT'2.0.0'# -*- coding: utf-8 -*-# Authors: Chitta Ranjan <cran2367@gmail.com>## License: BSD 3 clauseInstallation Test ExamplesIn the following, there are a few test examples to verify the installation.# Learning a sgt embedding as a matrix with# rows and columns as the sequence alphabets.# This embedding shows the relationship between# the alphabets. The higher the value the# stronger the relationship.sgt=SGT(flatten=False)sequence=np.array(["B","B","A","C","A","C","A","A","B","A"])sgt.fit(sequence)ABCA0.0906160.1310020.261849B0.0865690.1230420.052544C0.1371420.0282630.135335# SGT embedding to a vector. The vector# embedding is useful for directly applying# a machine learning algorithm.sgt=SGT(flatten=True)sequence=np.array(["B","B","A","C","A","C","A","A","B","A"])sgt.fit(sequence)(A, A)    0.090616
(A, B)    0.131002
(A, C)    0.261849
(B, A)    0.086569
(B, B)    0.123042
(B, C)    0.052544
(C, A)    0.137142
(C, B)    0.028263
(C, C)    0.135335
dtype: float64'''SGT embedding on a corpus of sequences.Test the two processing modes within theSGT class: 'default', 'multiprocessing'.'''# A sample corpus of two sequences.corpus=pd.DataFrame([[1,["B","B","A","C","A","C","A","A","B","A"]],[2,["C","Z","Z","Z","D"]]],columns=['id','sequence'])corpusidsequence01[B, B, A, C, A, C, A, A, B, A]12[C, Z, Z, Z, D]# Learning the sgt embeddings as vector for# all sequences in a corpus.# mode: 'default'sgt=SGT(kappa=1,flatten=True,lengthsensitive=False,mode='default')sgt.fit_transform(corpus)id(A, A)(A, B)(A, C)(A, D)(A, Z)(B, A)(B, B)(B, C)(B, D)...(D, A)(D, B)(D, C)(D, D)(D, Z)(Z, A)(Z, B)(Z, C)(Z, D)(Z, Z)01.00.0906160.1310020.2618490.00.00.0865690.1230420.0525440.0...0.00.00.00.00.00.00.00.00.0000000.00000012.00.0000000.0000000.0000000.00.00.0000000.0000000.0000000.0...0.00.00.00.00.00.00.00.00.1843340.2903652 rows × 26 columns# Learning the sgt embeddings as vector for# all sequences in a corpus.# mode: 'multiprocessing'importpandarallel# required library for multiprocessingsgt=SGT(kappa=1,flatten=True,lengthsensitive=False,mode='multiprocessing')sgt.fit_transform(corpus)INFO: Pandarallel will run on 7 workers.
INFO: Pandarallel will use standard multiprocessing data transfer (pipe) to transfer data between the main process and workers.id(A, A)(A, B)(A, C)(A, D)(A, Z)(B, A)(B, B)(B, C)(B, D)...(D, A)(D, B)(D, C)(D, D)(D, Z)(Z, A)(Z, B)(Z, C)(Z, D)(Z, Z)01.00.0906160.1310020.2618490.00.00.0865690.1230420.0525440.0...0.00.00.00.00.00.00.00.00.0000000.00000012.00.0000000.0000000.0000000.00.00.0000000.0000000.0000000.0...0.00.00.00.00.00.00.00.00.1843340.2903652 rows × 26 columnsLoad Libraries for Illustrative ExamplesfromsgtimportSGTimportnumpyasnpimportpandasaspdfromitertoolsimportchainfromitertoolsimportproductasiterproductimportwarningsimportpickle########fromsklearn.preprocessingimportLabelEncoderimporttensorflowastffromkeras.datasetsimportimdbfromtensorflow.keras.modelsimportSequentialfromtensorflow.keras.layersimportDensefromtensorflow.keras.layersimportLSTMfromtensorflow.keras.layersimportDropoutfromtensorflow.keras.layersimportActivationfromtensorflow.keras.layersimportFlattenfromtensorflow.keras.layersimportEmbeddingfromtensorflow.keras.preprocessingimportsequencefromsklearn.model_selectionimporttrain_test_splitfromsklearn.model_selectionimportKFoldfromsklearn.model_selectionimportStratifiedKFoldimportsklearn.metricsimporttimefromsklearn.decompositionimportPCAfromsklearn.clusterimportKMeansimportmatplotlib.pyplotasplt%matplotlibinlinenp.random.seed(7)# fix random seed for reproducibility# from sgt import SgtSequence ClusteringA form of unsupervised learning from sequences is clustering. For example, inuser weblogs sequences: clustering the weblogs segments users into groups with similar browsing behavior. This helps in targeted marketing, anomaly detection, and other web customizations.protein sequences: clustering proteins with similar structures help researchers study the commonalities between species. It also helps in faster search in some search algorithms.In the following, clustering on a protein sequence dataset will be shown.Protein Sequence ClusteringThe data used here is taken fromwww.uniprot.org. This is a public database for proteins. The data contains the protein sequences and their functions.# Loading datacorpus=pd.read_csv('data/protein_classification.csv')# Data preprocessingcorpus=corpus.loc[:,['Entry','Sequence']]corpus.columns=['id','sequence']corpus['sequence']=corpus['sequence'].map(list)corpusidsequence0M7MCX3[M, E, I, E, K, T, N, R, M, N, A, L, F, E, F, ...1K6PL84[M, E, I, E, K, N, Y, R, M, N, S, L, F, E, F, ...2R4W5V3[M, E, I, E, K, T, N, R, M, N, A, L, F, E, F, ...3T2A126[M, E, I, E, K, T, N, R, M, N, A, L, F, E, F, ...4L0SHD5[M, E, I, E, K, T, N, R, M, N, A, L, F, E, F, ............2107A0A081R612[M, M, N, M, Q, N, M, M, R, Q, A, Q, K, L, Q, ...2108A0A081QQM2[M, M, N, M, Q, N, M, M, R, Q, A, Q, K, L, Q, ...2109J1A517[M, M, R, Q, A, Q, K, L, Q, K, Q, M, E, Q, S, ...2110F5U1T6[M, M, N, M, Q, S, M, M, K, Q, A, Q, K, L, Q, ...2111J3A2T7[M, M, N, M, Q, N, M, M, K, Q, A, Q, K, L, Q, ...2112 rows × 2 columns%%time# Compute SGT embeddingssgt_=SGT(kappa=1,lengthsensitive=False,mode='multiprocessing')sgtembedding_df=sgt_.fit_transform(corpus)INFO: Pandarallel will run on 7 workers.
INFO: Pandarallel will use standard multiprocessing data transfer (pipe) to transfer data between the main process and workers.
CPU times: user 324 ms, sys: 68 ms, total: 392 ms
Wall time: 9.02 ssgtembedding_dfid(A, A)(A, C)(A, D)(A, E)(A, F)(A, G)(A, H)(A, I)(A, K)...(Y, M)(Y, N)(Y, P)(Y, Q)(Y, R)(Y, S)(Y, T)(Y, V)(Y, W)(Y, Y)0M7MCX30.0201800.00.0096350.0135290.0093600.0032052.944887e-100.0022260.000379...0.0091960.0079640.0367880.0001950.0015130.0206650.0005420.0074790.00.0104191K6PL840.0016040.00.0126370.0063230.0062240.0048193.560677e-030.0011240.012136...0.1353350.0065680.0389010.0112980.0125780.0099130.0010790.0000230.00.0077282R4W5V30.0124480.00.0084080.0163630.0274690.0032052.944887e-100.0042490.013013...0.0081140.0071280.0000000.0002030.0017570.0227360.0002490.0126520.00.0085333T2A1260.0105450.00.0125600.0142120.0137280.0000002.944887e-100.0072230.000309...0.0003250.0096690.0000000.0031820.0019040.0156070.0005770.0074790.00.0086484L0SHD50.0201800.00.0086280.0150330.0093600.0032052.944887e-100.0022260.000379...0.0091960.0079640.0367880.0001950.0015130.0206650.0005420.0074790.00.010419..................................................................2107A0A081R6120.0148050.00.0041590.0175410.0127010.0130990.000000e+000.0170430.004732...0.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.00.0000002108A0A081QQM20.0107740.00.0042830.0147320.0143400.0148460.000000e+000.0168060.005406...0.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.00.0000002109J1A5170.0107740.00.0042830.0147320.0143400.0148460.000000e+000.0145000.005406...0.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.00.0000002110F5U1T60.0152090.00.0051750.0238880.0114100.0115100.000000e+000.0211450.009280...0.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.00.0000002111J3A2T70.0052400.00.0123010.0131780.0147440.0147050.000000e+000.0009810.007957...0.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.00.0000002112 rows × 401 columns# Set the id column as the dataframe indexsgtembedding_df=sgtembedding_df.set_index('id')sgtembedding_df(A, A)(A, C)(A, D)(A, E)(A, F)(A, G)(A, H)(A, I)(A, K)(A, L)...(Y, M)(Y, N)(Y, P)(Y, Q)(Y, R)(Y, S)(Y, T)(Y, V)(Y, W)(Y, Y)idM7MCX30.0201800.00.0096350.0135290.0093600.0032052.944887e-100.0022260.0003790.021703...0.0091960.0079640.0367880.0001950.0015130.0206650.0005420.0074790.00.010419K6PL840.0016040.00.0126370.0063230.0062240.0048193.560677e-030.0011240.0121360.018427...0.1353350.0065680.0389010.0112980.0125780.0099130.0010790.0000230.00.007728R4W5V30.0124480.00.0084080.0163630.0274690.0032052.944887e-100.0042490.0130130.031118...0.0081140.0071280.0000000.0002030.0017570.0227360.0002490.0126520.00.008533T2A1260.0105450.00.0125600.0142120.0137280.0000002.944887e-100.0072230.0003090.028531...0.0003250.0096690.0000000.0031820.0019040.0156070.0005770.0074790.00.008648L0SHD50.0201800.00.0086280.0150330.0093600.0032052.944887e-100.0022260.0003790.021703...0.0091960.0079640.0367880.0001950.0015130.0206650.0005420.0074790.00.010419..................................................................A0A081R6120.0148050.00.0041590.0175410.0127010.0130990.000000e+000.0170430.0047320.014904...0.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.00.000000A0A081QQM20.0107740.00.0042830.0147320.0143400.0148460.000000e+000.0168060.0054060.014083...0.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.00.000000J1A5170.0107740.00.0042830.0147320.0143400.0148460.000000e+000.0145000.0054060.014083...0.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.00.000000F5U1T60.0152090.00.0051750.0238880.0114100.0115100.000000e+000.0211450.0092800.017466...0.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.00.000000J3A2T70.0052400.00.0123010.0131780.0147440.0147050.000000e+000.0009810.0079570.017112...0.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.00.0000002112 rows × 400 columnsWe perform PCA on the sequence embeddings and then do kmeans clustering.pca=PCA(n_components=2)pca.fit(sgtembedding_df)X=pca.transform(sgtembedding_df)print(np.sum(pca.explained_variance_ratio_))df=pd.DataFrame(data=X,columns=['x1','x2'])df.head()0.6432744907364981x1x200.384913-0.26987310.0227640.13599520.177792-0.17245430.168074-0.14733440.383616-0.271163kmeans=KMeans(n_clusters=3,max_iter=300)kmeans.fit(df)labels=kmeans.predict(df)centroids=kmeans.cluster_centers_fig=plt.figure(figsize=(5,5))colmap={1:'r',2:'g',3:'b'}colors=list(map(lambdax:colmap[x+1],labels))plt.scatter(df['x1'],df['x2'],color=colors,alpha=0.5,edgecolor=colors)<matplotlib.collections.PathCollection at 0x14c5a77f0>Sequence Classification using Deep Learning in TensorFlowThe protein data set used above is also labeled. The labels represent the protein functions. Similarly, there are other labeled sequence data sets. For example, DARPA shared an intrusion weblog data set. It contains weblog sequences with positive labels if the log represents a network intrusion.In such problems supervised learning is employed. Classification is a supervised learning we will demonstrate here.Protein Sequence ClassificationThe data set is taken fromhttps://www.uniprot.org. The protein sequences in the data set have one of the two functions,Binds to DNA and alters its conformation. May be involved in regulation of gene expression, nucleoid organization and DNA protection.Might take part in the signal recognition particle (SRP) pathway. This is inferred from the conservation of its genetic proximity to ftsY/ffh. May be a regulatory protein.There are a total of 2113 samples. The sequence lengths vary between 100-700.# Loading datadata=pd.read_csv('data/protein_classification.csv')# Data preprocessingy=data['Function [CC]']encoder=LabelEncoder()encoder.fit(y)encoded_y=encoder.transform(y)corpus=data.loc[:,['Entry','Sequence']]corpus.columns=['id','sequence']corpus['sequence']=corpus['sequence'].map(list)Sequence embeddings# Sequence embeddingsgt_=SGT(kappa=1,lengthsensitive=False,mode='multiprocessing')sgtembedding_df=sgt_.fit_transform(corpus)X=sgtembedding_df.set_index('id')INFO: Pandarallel will run on 7 workers.
INFO: Pandarallel will use standard multiprocessing data transfer (pipe) to transfer data between the main process and workers.We will perform a 10-fold cross-validation to measure the performance of the classification model.kfold=10X=Xy=encoded_yrandom_state=1test_F1=np.zeros(kfold)skf=KFold(n_splits=kfold,shuffle=True,random_state=random_state)k=0epochs=50batch_size=128fortrain_index,test_indexinskf.split(X,y):X_train,X_test=X.iloc[train_index],X.iloc[test_index]y_train,y_test=y[train_index],y[test_index]model=Sequential()model.add(Dense(64,input_shape=(X_train.shape[1],)))model.add(Activation('relu'))model.add(Dropout(0.5))model.add(Dense(32))model.add(Activation('relu'))model.add(Dropout(0.5))model.add(Dense(1))model.add(Activation('sigmoid'))model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])model.fit(X_train,y_train,batch_size=batch_size,epochs=epochs,verbose=0)y_pred=model.predict_proba(X_test).round().astype(int)y_train_pred=model.predict_proba(X_train).round().astype(int)test_F1[k]=sklearn.metrics.f1_score(y_test,y_pred)k+=1print('Average f1 score',np.mean(test_F1))Average f1 score 1.0Weblog Classification for Intrusion DetectionThis data sample is taken fromhttps://www.ll.mit.edu/r-d/datasets/1998-darpa-intrusion-detection-evaluation-dataset.
This is a network intrusion data containing audit logs and any attack as a positive label. Since, network intrusion is a rare event, the data is unbalanced. Here we will,build a sequence classification model to predict a network intrusion.Each sequence contains in the data is a series of activity, for example, {login, password}. Thealphabetsin the input data sequences are already encoded into integers. The original sequences data file is also present in the/datadirectory.# Loading datadata=pd.read_csv('data/darpa_data.csv')data.columnsIndex(['timeduration', 'seqlen', 'seq', 'class'], dtype='object')data['id']=data.index# Data preprocessingy=data['class']encoder=LabelEncoder()encoder.fit(y)encoded_y=encoder.transform(y)corpus=data.loc[:,['id','seq']]corpus.columns=['id','sequence']corpus['sequence']=corpus['sequence'].map(list)Sequence embeddingsIn this data, the sequence embeddings should belength-sensitive.The lengths are important here because sequences with similar patterns but different lengths can have different labels. Consider a simple example of two sessions:{login, pswd, login, pswd,...}and{login, pswd,...(repeated several times)..., login, pswd}.While the first session can be a regular user mistyping the password once, the other session is possibly an attack to guess the password. Thus, the sequence lengths are as important as the patterns.Therefore,lengthsensitive=Trueis used here.# Sequence embeddingsgt_=SGT(kappa=5,lengthsensitive=True,mode='multiprocessing')sgtembedding_df=sgt_.fit_transform(corpus)sgtembedding_df=sgtembedding_df.set_index('id')sgtembedding_dfINFO: Pandarallel will run on 7 workers.
INFO: Pandarallel will use standard multiprocessing data transfer (pipe) to transfer data between the main process and workers.(0, 0)(0, 1)(0, 2)(0, 3)(0, 4)(0, 5)(0, 6)(0, 7)(0, 8)(0, 9)...(~, 1)(~, 2)(~, 3)(~, 4)(~, 5)(~, 6)(~, 7)(~, 8)(~, 9)(~, ~)id0.00.0000000.0000000.0000000.000000e+000.000000e+000.000000e+000.0000000.000000e+000.000000e+000.000000e+00...0.4850340.4869990.4858020.4830970.4839560.0000000.0000000.0000000.0000000.1786091.00.0000000.0256220.2281560.000000e+000.000000e+001.310714e-090.0000000.000000e+000.000000e+000.000000e+00...0.4476200.4520970.4645680.3672960.5251410.4550180.3743640.4140810.5499810.1724792.00.0000000.0000000.0000000.000000e+000.000000e+000.000000e+000.0000000.000000e+000.000000e+000.000000e+00...0.5256050.0000000.0000000.0000000.0000000.0000000.0000000.0000000.1933590.0714693.00.0779990.2089740.2303381.830519e-011.200926e-171.696880e-010.0936467.985870e-022.896813e-053.701710e-05...0.4740720.4683530.4635940.1775070.5512700.4186520.3096520.3846570.3782250.1703624.00.0000000.0236950.2178192.188276e-330.000000e+006.075992e-110.0000000.000000e+005.681668e-390.000000e+00...0.4641200.4682290.4521700.0000000.5012420.0000000.3005340.1619610.0000000.167082..................................................................106.00.0000000.0244950.2199292.035190e-171.073271e-185.656994e-110.0000000.000000e+005.047380e-290.000000e+00...0.5022130.5443430.4772810.1759010.4611030.0000000.0000000.1627960.0000000.167687107.00.1104220.2274780.2175491.723963e-011.033292e-143.896725e-070.0836852.940589e-088.864072e-024.813990e-29...0.4903980.5220160.4668080.4706030.4797950.4800570.1948880.1723970.1648730.172271108.00.0056460.2024240.1967862.281242e-011.133936e-011.862098e-010.0000001.212869e-019.180520e-080.000000e+00...0.4328340.4349530.4396150.3908640.4817640.6008750.1667660.1653680.0000000.171729109.00.0000000.0256160.2381763.889176e-551.332427e-601.408003e-090.0000009.845377e-600.000000e+000.000000e+00...0.4213180.4399850.4679530.4409510.5271650.8647170.4071550.3993350.2513040.171885110.00.0000000.0228680.2035139.273472e-640.000000e+001.240870e-090.0000000.000000e+000.000000e+000.000000e+00...0.4780900.4548710.4591090.0000000.4905340.3703570.0000000.1629970.0000000.162089111 rows × 121 columnsApplying PCA on the embeddingsThe embeddings are sparse and high-dimensional. PCA is, therefore, applied for dimension reduction.fromsklearn.decompositionimportPCApca=PCA(n_components=35)pca.fit(sgtembedding_df)X=pca.transform(sgtembedding_df)print(np.sum(pca.explained_variance_ratio_))0.9962446146783123Building a Multi-Layer Perceptron ClassifierThe PCA transforms of the embeddings are used directly as inputs to an MLP classifier.kfold=3random_state=11X=Xy=encoded_ytest_F1=np.zeros(kfold)time_k=np.zeros(kfold)skf=StratifiedKFold(n_splits=kfold,shuffle=True,random_state=random_state)k=0epochs=300batch_size=15# class_weight = {0 : 1., 1: 1.,}  # The weights can be changed and made inversely proportional to the class size to improve the accuracy.class_weight={0:0.12,1:0.88,}fortrain_index,test_indexinskf.split(X,y):X_train,X_test=X[train_index],X[test_index]y_train,y_test=y[train_index],y[test_index]model=Sequential()model.add(Dense(128,input_shape=(X_train.shape[1],)))model.add(Activation('relu'))model.add(Dropout(0.5))model.add(Dense(1))model.add(Activation('sigmoid'))model.summary()model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])start_time=time.time()model.fit(X_train,y_train,batch_size=batch_size,epochs=epochs,verbose=1,class_weight=class_weight)end_time=time.time()time_k[k]=end_time-start_timey_pred=model.predict_proba(X_test).round().astype(int)y_train_pred=model.predict_proba(X_train).round().astype(int)test_F1[k]=sklearn.metrics.f1_score(y_test,y_pred)k+=1Model: "sequential_10"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_30 (Dense)             (None, 128)               4608      
_________________________________________________________________
activation_30 (Activation)   (None, 128)               0         
_________________________________________________________________
dropout_20 (Dropout)         (None, 128)               0         
_________________________________________________________________
dense_31 (Dense)             (None, 1)                 129       
_________________________________________________________________
activation_31 (Activation)   (None, 1)                 0         
=================================================================
Total params: 4,737
Trainable params: 4,737
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:sample_weight modes were coerced from
  ...
    to  
  ['...']
Train on 74 samples
Epoch 1/300
74/74 [==============================] - 0s 7ms/sample - loss: 0.1487 - accuracy: 0.5270
Epoch 2/300
74/74 [==============================] - 0s 120us/sample - loss: 0.1421 - accuracy: 0.5000
...
74/74 [==============================] - 0s 118us/sample - loss: 0.0299 - accuracy: 0.8784
Epoch 300/300
74/74 [==============================] - 0s 133us/sample - loss: 0.0296 - accuracy: 0.8649print('Average f1 score',np.mean(test_F1))print('Average Run time',np.mean(time_k))Average f1 score 0.6341880341880342
Average Run time 3.880180994669596Building an LSTM Classifier on the sequences for comparisonWe built an LSTM Classifier on the sequences to compare the accuracy.X=data['seq']encoded_X=np.ndarray(shape=(len(X),),dtype=list)foriinrange(0,len(X)):encoded_X[i]=X.iloc[i].split("~")X0      1~2~3~3~3~3~3~3~1~4~5~1~2~3~3~3~3~3~3~1~4~5~1~...
1      6~5~5~6~5~6~5~2~5~5~5~5~5~5~5~5~5~5~5~5~5~5~5~...
2      19~19~19~19~19~19~19~19~19~19~19~19~19~19~19~1...
3      6~5~5~6~5~6~5~2~5~5~5~5~5~5~5~5~5~5~5~5~5~5~5~...
4      5~5~17~5~5~5~5~5~10~2~11~2~11~11~12~11~11~5~2~...
                             ...                        
106    10~2~11~2~11~11~12~11~11~5~2~11~5~2~5~2~3~14~3...
107    5~5~2~5~17~6~5~6~5~5~2~6~17~3~2~2~3~5~2~3~5~6~...
108    6~5~6~5~5~6~5~5~6~6~6~6~6~6~6~6~6~6~6~6~6~6~6~...
109    6~5~5~6~5~6~5~2~38~2~3~5~22~39~5~5~5~5~5~5~5~5...
110    5~6~5~5~10~2~11~2~11~11~12~11~5~2~11~11~12~11~...
Name: seq, Length: 111, dtype: objectmax_seq_length=np.max(data['seqlen'])encoded_X=tf.keras.preprocessing.sequence.pad_sequences(encoded_X,maxlen=max_seq_length)kfold=3random_state=11test_F1=np.zeros(kfold)time_k=np.zeros(kfold)epochs=50batch_size=15skf=StratifiedKFold(n_splits=kfold,shuffle=True,random_state=random_state)k=0fortrain_index,test_indexinskf.split(encoded_X,y):X_train,X_test=encoded_X[train_index],encoded_X[test_index]y_train,y_test=y[train_index],y[test_index]embedding_vecor_length=32top_words=50model=Sequential()model.add(Embedding(top_words,embedding_vecor_length,input_length=max_seq_length))model.add(LSTM(32))model.add(Dense(1))model.add(Activation('sigmoid'))model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])model.summary()start_time=time.time()model.fit(X_train,y_train,epochs=epochs,batch_size=batch_size,verbose=1)end_time=time.time()time_k[k]=end_time-start_timey_pred=model.predict_proba(X_test).round().astype(int)y_train_pred=model.predict_proba(X_train).round().astype(int)test_F1[k]=sklearn.metrics.f1_score(y_test,y_pred)k+=1Model: "sequential_13"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (None, 1773, 32)          1600      
_________________________________________________________________
lstm (LSTM)                  (None, 32)                8320      
_________________________________________________________________
dense_36 (Dense)             (None, 1)                 33        
_________________________________________________________________
activation_36 (Activation)   (None, 1)                 0         
=================================================================
Total params: 9,953
Trainable params: 9,953
Non-trainable params: 0
_________________________________________________________________
Train on 74 samples
Epoch 1/50
74/74 [==============================] - 5s 72ms/sample - loss: 0.6894 - accuracy: 0.5676
Epoch 2/50
74/74 [==============================] - 4s 48ms/sample - loss: 0.6590 - accuracy: 0.8784
...
Epoch 50/50
74/74 [==============================] - 4s 51ms/sample - loss: 0.1596 - accuracy: 0.9324print('Average f1 score',np.mean(test_F1))print('Average Run time',np.mean(time_k))Average f1 score 0.36111111111111116
Average Run time 192.46954011917114We find that the LSTM classifier gives a significantly lower F1 score. This may be improved by changing the model. However, we find that the SGT embedding could work with a small and unbalanced data without the need of a complicated classifier model.LSTM models typically require more data for training and also has significantly more computation time. The LSTM model above took 425.6 secs while the MLP model took just 9.1 secs.Sequence SearchSequence data sets are generally large. For example, sequences of listening history in music streaming services, such as Pandora, for more than 70M users are huge. In protein data bases there could be even larger size. For instance, the Uniprot data repository has more than 177M sequences.Searching for similar sequences in such large data bases is challenging. SGT embedding provides a simple solution. In the following it will be shown on a protein data set that SGT embedding can be used to compute similarity between a query sequence and the sequence corpus using a dot product. The sequences with the highest dot product are returned as the most similar sequence to the query.Protein Sequence SearchIn the following, a sample of 10k protein sequences are used for illustration. The data is taken fromhttps://www.uniprot.org.# Loading datadata=pd.read_csv('data/protein-uniprot-reviewed-Ano-10k.tab',sep='\t')# Data preprocessingcorpus=data.loc[:,['Entry','Sequence']]corpus.columns=['id','sequence']corpus['sequence']=corpus['sequence'].map(list)corpus.head(3)idsequence0I2WKR6[M, V, H, K, S, D, S, D, E, L, A, A, L, R, A, ...1A0A2A6M8K9[M, Q, E, S, L, V, V, R, R, E, T, H, I, A, A, ...2A0A3G5KEC3[M, A, S, G, A, Y, S, K, Y, L, F, Q, I, I, G, ...# Protein sequence alphabetsalphabets=['A','C','D','E','F','G','H','I','K','L','M','N','P','Q','R','S','T','V','W','X','Y','U','O']# List of amino acids# Alphabets are known and inputted# as arguments for faster computationsgt_=SGT(alphabets=alphabets,lengthsensitive=True,kappa=1,flatten=True,mode='multiprocessing')sgtembedding_df=sgt_.fit_transform(corpus)sgtembedding_df=sgtembedding_df.set_index('id')INFO: Pandarallel will run on 7 workers.
INFO: Pandarallel will use standard multiprocessing data transfer (pipe) to transfer data between the main process and workers.'''Search proteins similar to a query protein.The approach is to find the SGT embedding of thequery protein and find its similarity with theembeddings of the protein database.'''query_protein='MSHVFPIVIDDNFLSPQDLVSAARSGCSLRLHTGVVDKIDRAHRFVLEIAGAEALHYGINTGFGSLCTTHIDPADLSTLQHNLLKSHACGVGPTVSEEVSRVVTLIKLLTFRTGNSGVSLSTVNRIIDLWNHGVVGAIAQKGTVGASGDLAPLAHLFLPLIGLGQVWHRGVLRPSREVMDELKLAPLTLQPKDGLCLTNGVQYLNAWGALSTVRAKRLVALADLCAAMSMMGFSAARSFIEAQIHQTSLHPERGHVALHLRTLTHGSNHADLPHCNPAMEDPYSFRCAPQVHGAARQVVGYLETVIGNECNSVSDNPLVFPDTRQILTCGNLHGQSTAFALDFAAIGITDLSNISERRTYQLLSGQNGLPGFLVAKPGLNSGFMVVQYTSAALLNENKVLSNPASVDTIPTCHLQEDHVSMGGTSAYKLQTILDNCETILAIELMTACQAIDMNPGLQLSERGRAIYEAVREEIPFVKEDHLMAGLISKSRDLCQHSTVIAQQLAEMQAQ'# Step 1. Compute sgt embedding for the query protein.query_protein_sgt_embedding=sgt_.fit(list(query_protein))# Step 2. Compute the dot product of query embedding# with the protein embedding database.similarity=sgtembedding_df.dot(query_protein_sgt_embedding)# Step 3. Return the top k protein names based on similarity.similarity.sort_values(ascending=False)id
K0ZGN5        2773.749663
A0A0Y1CPH7    1617.451379
A0A5R8LCJ1    1566.833152
A0A290WY40    1448.772820
A0A073K6N6    1392.267250
                 ...     
A0A1S7UBK4     160.074989
A0A2S7T1R9     156.580584
A0A0E0UQV6     155.834932
A0A1Y5Y0S0     148.862049
B0NRP3         117.656497
Length: 10063, dtype: float64SGT - Spark for Distributed ComputingAs mentioned in the previous section, sequence data sets can be large. SGT complexity is linear with the number of sequences in a data set. Still if the data size is large the computation becomes high. For example, for a set of 1M protein sequences the default SGT mode takes over 24 hours.Using distributed computing with Spark the runtime can be significantly reduced. For instance, SGT-Spark on the same 1M protein data set took less than 29 minutes.In the following, Spark implementation for SGT is shown. First, it is applied on a smaller 10k data set for comparison. Then it is applied on 1M data set without any syntactical change.'''Load the data and remove header.'''data=sc.textFile('data/protein-uniprot-reviewed-Ano-10k.tab')header=data.first()#extract headerdata=data.filter(lambdarow:row!=header)#filter out headerdata.take(1)# See one sampleOut[3]:['I2WKR6\tI2WKR6_ECOLX\tunreviewed\tType III restriction enzyme, res subunit (EC 3.1.21.5)\tEC90111_4246\tEscherichia coli 9.0111\t786\tMVHKSDSDELAALRAENVRLVSLLEAHGIEWRRKPQSPVPRVSVLSTNEKVALFRRLFRGRDDVWALRWESKTSGKSGYSPACANEWQLGICGKPRIKCGDCAHRQLIPVSDLVIYHHLAGTHTAGMYPLLEDDSCYFLAVDFDEAEWQKDASAFMRSCDELGVPAALEISRSRQGAHVWIFFASRVSAREARRLGTAIISYTCSRTRQLRLGSYDRLFPNQDTMPKGGFGNLIALPLQKRPRELGGSVFVDMNLQPYPDQWAFLVSVIPMNVQDIEPTILRATGSIHPLDVNFINEEDLGTPWEEKKSSGNRLNIAVTEPLIITLANQIYFEKAQLPQALVNRLIRLAAFPNPEFYKAQAMRMSVWNKPRVIGCAENYPQHIALPRGCLDSALSFLRYNNIAAELIDKRFAGTECNAVFTGNLRAEQEEAVSALLRYDTGVLCAPTAFGKTVTAAAVIARRKVNTLILVHRTELLKQWQERLAVFLQVGDSIGIIGGGKHKPCGNIDIAVVQSISRHGEVEPLVRNYGQIIVDECHHIGAVSFSAILKETNARYLLGLTATPIRRDGLHPIIFMYCGAIRHTAARPKESLHNLEVLTRSRFTSGHLPSDARIQDIFREIALDHDRTVAIAEEAMKAFGQGRKVLVLTERTDHLDDIASVMNTLKLSPFVLHSRLSKKKRTMLISGLNALPPDSPRILLSTGRLIGEGFDHPPLDTLILAMPVSWKGTLQQYAGRLHREHTGKSDVRIIDFVDTAYPVLLRMWDKRQRGYKAMGYRIVADGEGLSF']# Repartition for increasing the parallel processesdata=data.repartition(500)defpreprocessing(line):'''Original data are lines where each line has \tseparated values. We are interested in preservingthe first value (entry id), tmp[0], and the last value(the sequence), tmp[-1].'''tmp=line.split('\t')id=tmp[0]sequence=list(tmp[-1])return(id,sequence)processeddata=data.map(lambdaline:preprocessing(line))processeddata.take(1)# See one sampleOut[5]:[('A0A2E9WIJ1',
  ['M',
   'Y',
   'I',
   'F',
   'L',
   'T',
   'L',
	...   
   'A',
   'K',
   'L',
   'D',
   'K',
   'N',
   'D'])]# Protein sequence alphabetsalphabets=['A','C','D','E','F','G','H','I','K','L','M','N','P','Q','R','S','T','V','W','X','Y','U','O']# List of amino acids'''Spark approach.In this approach the alphabets argument has tobe passed to the SGT class definition.The SGT.fit() is then called in parallel.'''sgt_=sgt.SGT(alphabets=alphabets,kappa=1,lengthsensitive=True,flatten=True)rdd=processeddata.map(lambdax:(x[0],list(sgt_.fit(x[1]))))sgtembeddings=rdd.collect()# Command took 29.66 seconds -- by cranjan@processminer.com at 4/22/2020, 12:31:23 PM on databricksCompare with the default SGT mode# Loading datadata=pd.read_csv('data/protein-uniprot-reviewed-Ano-10k.tab',sep='\t')# Data preprocessingcorpus=data.loc[:,['Entry','Sequence']]corpus.columns=['id','sequence']corpus['sequence']=corpus['sequence'].map(list)sgt_=sgt.SGT(alphabets=alphabets,lengthsensitive=True,kappa=1,flatten=True,mode='default')sgtembedding_df=sgt_.fit_transform(corpus)# Command took 13.08 minutes -- by cranjan@processminer.com at 4/22/2020, 1:48:02 PM on databricks1M Protein DatabaseProtein 1M sequence data set is embedded here. The data set is availablehere.'''Load the data and remove header.'''data=sc.textFile('data/protein-uniprot-reviewed-Ano-1M.tab')header=data.first()#extract headerdata=data.filter(lambdarow:row!=header)#filter out headerdata.take(1)# See one sample# Repartition for increasing the parallel processesdata=data.repartition(10000)processeddata=data.map(lambdaline:preprocessing(line))processeddata.take(1)# See one sample# [('A0A2E9WIJ1',#   ['M','Y','I','F','L','T','L','A','L','F','S',...,'F','S','I','F','A','K','L','D','K','N','D'])]# Protein sequence alphabetsalphabets=['A','C','D','E','F','G','H','I','K','L','M','N','P','Q','R','S','T','V','W','X','Y','U','O']# List of amino acids'''Spark approach.In this approach the alphabets argument has tobe passed to the SGT class definition.The SGT.fit() is then called in parallel.'''sgt_=sgt.SGT(alphabets=alphabets,kappa=1,lengthsensitive=True,flatten=True)rdd=processeddata.map(lambdax:(x[0],list(sgt_.fit(x[1]))))sgtembeddings=rdd.collect()# Command took 28.98 minutes -- by cranjan@processminer.com at 4/22/2020, 3:16:41 PM on databricks'''OPTIONAL.Save the embeddings for future use orproduction deployment.'''# Save for deployment# pickle.dump(sgtembeddings,#             open("data/protein-sgt-embeddings-1M.pkl", "wb"))# The pickle dump is shared at https://mega.nz/file/hiAxAAoI#SStAIn_FZjAHvXSpXfdy8VpISG6rusHRf9HlUSqwcsw# sgtembeddings = pickle.load(open("data/protein-sgt-embeddings-1M.pkl", "rb"))The pickle dump is sharedhere.Sequence Search using SGT - SparkSincesgtembeddingson the 1M data set is large it is recommended to use distributed computing to find similar proteins during a search.sgtembeddings_rdd=sc.parallelize(list(dict(sgtembeddings).items()))sgtembeddings_rdd=sgtembeddings_rdd.repartition(10000)'''Search proteins similar to a query protein.The approach is to find the SGT embedding of thequery protein and find its similarity with theembeddings of the protein database.'''query_protein='MSHVFPIVIDDNFLSPQDLVSAARSGCSLRLHTGVVDKIDRAHRFVLEIAGAEALHYGINTGFGSLCTTHIDPADLSTLQHNLLKSHACGVGPTVSEEVSRVVTLIKLLTFRTGNSGVSLSTVNRIIDLWNHGVVGAIAQKGTVGASGDLAPLAHLFLPLIGLGQVWHRGVLRPSREVMDELKLAPLTLQPKDGLCLTNGVQYLNAWGALSTVRAKRLVALADLCAAMSMMGFSAARSFIEAQIHQTSLHPERGHVALHLRTLTHGSNHADLPHCNPAMEDPYSFRCAPQVHGAARQVVGYLETVIGNECNSVSDNPLVFPDTRQILTCGNLHGQSTAFALDFAAIGITDLSNISERRTYQLLSGQNGLPGFLVAKPGLNSGFMVVQYTSAALLNENKVLSNPASVDTIPTCHLQEDHVSMGGTSAYKLQTILDNCETILAIELMTACQAIDMNPGLQLSERGRAIYEAVREEIPFVKEDHLMAGLISKSRDLCQHSTVIAQQLAEMQAQ'# Step 1. Compute sgt embedding for the query protein.query_protein_sgt_embedding=sgt_.fit(list(query_protein))# Step 2. Broadcast the embedding to the cluster.query_protein_sgt_embedding_broadcasted=sc.broadcast(list(query_protein_sgt_embedding))# Step 3. Compute similarity between each sequence embedding and the query.similarity=sgtembeddings_rdd.map(lambdax:(x[0],np.dot(query_protein_sgt_embedding_broadcasted.value,x[1]))).collect()# Step 4. Show the most similar sequences with the query.pd.DataFrame(similarity).sort_values(by=1,ascending=False)