<jupyter_start><jupyter_text>**Neural Collaborative Filtering for Drug prediction**  Machine learning can be used in all aspects of human life,New Drug discovery can be very expensive and can take up to a year, Hence it is very beneficial to find the drug that can be used to treat the specific medical condition from the existing drugs.This idea is similar to the idea of  recommendation systems where the item is recommended to the user based on certain factors Similarly, we can recommend the drug for the treatment of certain medical conditions based on certain factors.To do so, here we are using the Nural collaborative filtering approach to predict the drugs for a specific medical condition.We will be using the Multilayer perceptron model as proposed in a research  paper[1]We have done certain modifications to match the current needs. Now let’s start  **Importing all necessory liberies**<jupyter_code>import sys 
import multiprocessing
from time import time



import numpy as np
import pandas as pd
import random
import math
import argparse
import heapq
import scipy.sparse as sp



import theano
import theano.tensor as T
import tensorflow as tf
import keras

from keras import layers
from keras.models import Sequential,Model
from keras import backend as K
from keras import initializers
from keras.regularizers import l1, l2 
from keras.layers import Dense, Lambda, Activation
from keras.layers import Embedding, Input, Dense, merge ,Reshape, Flatten, Dropout
from keras.optimizers import Adagrad, Adam, SGD, RMSprop<jupyter_output><empty_output><jupyter_text>**Dataset**we are using drug_com data set which you can download from Kaggle [Here]( https://www.kaggle.com/jessicali9530/kuc-hackathon-winter-2018)This dataset includes the different drugs and their effectiveness or uses in different medical conditions.Now let’s analyze the dataset to get an understanding of the data.<jupyter_code>import pandas as pd
df = pd.read_csv('/content/drive/My Drive/76158_171475_compressed_drugsComTrain_raw.csv.zip').drop(['uniqueID','date'],axis = 1)
df
 
print(len(df['condition'].unique()))
print(len(df['drugName'].unique()))
Medical_Conditions = df['condition'].unique()
Drugs = df['drugName'].unique()<jupyter_output><empty_output><jupyter_text>.Our dataset contains the drug name and respective medical condition, with all other information like a review , rating, useful count ... Now we want to find the usefulness of a particular drug in the different conditions ( i.e predicting the drug  for certain condition) Here we can see that we have 3436 unique drugs with 885 unique conditions and also 161297 interactions.  **Mapping the drug names to Unique IDs and Unique IDs to  drug names**<jupyter_code>Medical_Conditions_ID_to_name= {}

for i in range(len(Medical_Conditions)):
  key = i
  value = Medical_Conditions[i]
  Medical_Conditions_ID_to_name[key] = value

Drugs_ID_to_name = {}


for i in range(len(Drugs)):
  key = i
  value = Drugs[i]
  Drugs_ID_to_name[key] = value

Medical_Conditions_ID_to_NAME = pd.DataFrame(list(Medical_Conditions_ID_to_name.items()))
Drugs_ID_to_NAME = pd.DataFrame(list(Drugs_ID_to_name.items()))

Medical_Conditions_ID_to_NAME.to_csv('Medical_Conditions_ID_to_NAME.csv')
Drugs_ID_to_NAME.to_csv('Drugs_ID_to_NAME.csv')
Medical_Conditions_name_to_ID =  dict([(value, key) for key, value in Medical_Conditions_ID_to_name.items()])
Drugs_name_to_ID =  dict([(value, key) for key, value in Drugs_ID_to_name.items()])<jupyter_output><empty_output><jupyter_text>Considering the useful columns(Here we are using the usefulcount as the interaction between drug and specific medical condition )<jupyter_code>df = df[['condition','drugName','usefulCount']].copy()
df<jupyter_output><empty_output><jupyter_text>Mapping<jupyter_code>for i in range(len(df['drugName'])):
  df['drugName'][i] = Drugs_name_to_ID[df['drugName'][i]]

for i in range(len(df['condition'])):
  df['condition'][i] = Medical_Conditions_name_to_ID[df['condition'][i]]

df.sort_values("condition", axis = 0, ascending = True, 
                 inplace = True)

df<jupyter_output>/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  
/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:5: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  """<jupyter_text>As this is a binary classification problem we are not considering the properties of drugs ( can be done in the next version )We are going to build the sparse matrix for representing interactions between drugs and medical conditions For testing, we have taken 200 users with their positive, negative interactions (Please have a look at "log loss with negative sampling" part in the referred research paper[1]<jupyter_code>def get_data():
  '''
  return :  train and test data
  train-> sparce matrix
  test-> matrix (list of lists)
  '''

  train = sp.dok_matrix((885, 3436), dtype=np.float32)
 
  for i in  range(len(df['condition'])):
    ls = list(df.iloc[i])
    train[ls[0],ls[1]] = 1.0


  test = []

  for j in  range(200):
    i = random.randint(0, 161296)
    ls = list(df.iloc[i])
    test.append([ls[0],ls[1]])

  return train , test<jupyter_output><empty_output><jupyter_text>**Evaluation Process**To evaluate the performance ofdrug recommendation, we adopted the leave-one-out evaluation.as described in the referred research paper[1] HR intuitively measures whetherthe test item is present on the top-10 list, and the NDCGaccounts for the position of the hit by assigning higher scoresto hits at top ranks. We will calculate both metrics for eachtest user and asssine the average score.<jupyter_code>def getHitRatio(ranklist, gtItem):
    for item in ranklist:
        if item == gtItem:
            return 1
    return 0

def getNDCG(ranklist, gtItem):
    for i in range(len(ranklist)):
        item = ranklist[i]
        if item == gtItem:
            return math.log(2) / math.log(i+2)
    return 0
    


def evaluate(model,K):

  HR, NDCG = [],[]
  train,test = get_data()


  for i in range(len(test)):                                              

    rating = test[i]                                                     
    u = rating[0]                                                       

    # taking 99 randome untested conditions by that drug
    count = 0
    drugs = [] 
    while(count != 99):
      j = random.randint(0, 3435)     
      if (u,j) in train.keys():
        continue
      drugs.append(j)
      count+=1
                                 
                                                  
                                                         
    gtdrug = rating[1]                                                     
    drugs.append(gtdrug)                                                   
    
    
    
    # Get prediction scores
    map_drug_score = {}
    medical_conditions = np.full(len(drugs), u, dtype = 'int32')
    predictions = model.predict([medical_conditions, np.array(drugs)], 
                                 batch_size=64, verbose=0)               
    
    
    for i in range(len(drugs)):                                            
        drug = drugs[i]
        map_drug_score[drug] = predictions[i]

    drugs.pop()                                                             
    
     
    ranklist = heapq.nlargest(K, map_drug_score, key=map_drug_score.get)   
    hr = getHitRatio(ranklist, gtdrug)                                       
    ndcg = getNDCG(ranklist, gtdrug)

    HR.append(hr)
    NDCG.append(ndcg) 

  return (HR, NDCG)<jupyter_output><empty_output><jupyter_text>**Prepareing model****Multi-Layer Presepteron** :( as described in the paper[1]) Since NCF adopts two pathways to model drugs and conditions,it is intuitive to combine the features of two pathways byconcatenating them. This design has been widely adoptedin multimodal deep learning works. However, simplya vector concatenation does not account for any interactionsbetween drug and condition latent features, which is insufficientfor modeling the collaborative filtering effect. To addressthis issue, we will add hidden layers on the concatenated vector, using a standard MLP to learn the interactionbetween drug and condition latent features. In this sense, we canendow the model a large level of **flexibility and non-linearity.**<jupyter_code>def get_model(num_medical_conditions, num_drugs, layers = [16,8], reg_layers=[0,0]):

    assert len(layers) == len(reg_layers)

    num_layer = len(layers)                                                                      #Number of layers in the MLP
    
    medical_condition_input = Input(shape=(1,), dtype='int32', name = 'user_input')
    drug_input = Input(shape=(1,), dtype='int32', name = 'item_input')


    MLP_Embedding_Medical_Conditions = Embedding(input_dim = num_medical_conditions, output_dim = int(layers[0]/2), 
                                   name = 'medical_condition_embedding',
                                    W_regularizer = l2(reg_layers[0]), input_length=1)
    MLP_Embedding_Drugs = Embedding(input_dim = num_drugs, output_dim = int(layers[0]/2), 
                                   name = 'drug_embedding',
                                    W_regularizer = l2(reg_layers[0]), input_length=1)   
    

   
    medical_condition_latent = Flatten()(MLP_Embedding_Medical_Conditions(medical_condition_input))                                      # flattening embedding for user 
    drug_latent = Flatten()(MLP_Embedding_Drugs(drug_input))                                      # flattening embedding for items
     

    vector = keras.layers.concatenate([medical_condition_latent,drug_latent])                                 # forming the 0th layer of NN by concatinating the user and items flatten layer 
    
    # MLP layers
    for idx in range(1, num_layer):
        layer = Dense(layers[idx], W_regularizer= l2(reg_layers[idx]), activation='relu', name = 'layer%d' %idx)
        vector = layer(vector)
        #layer1 = Dropout(0.25)
        #vector = layer1(vector)
        
    
    
        
    # Final prediction layer
    prediction = Dense(1, activation='sigmoid', init='lecun_uniform', name = 'prediction')(vector)
    
    model = Model(input=[medical_condition_input, drug_input], 
                  output=prediction)
    
    return model



def get_train_instances(train, num_negatives):
  
    medical_condition_input, drug_input, labels = [],[],[]
    num_medical_conditions = train.shape[0]

    for (u, i) in train.keys():

        # positive instance
        medical_condition_input.append(u)
        drug_input.append(i)
        labels.append(1)


        # negative instances
        for t in range(num_negatives):
            j = np.random.randint(num_drugs)
            
            while ( (u,j) in train.keys() ) :
                j = np.random.randint(num_drugs)
            medical_condition_input.append(u)
            drug_input.append(j)
            labels.append(0)
        
        
    return medical_condition_input, drug_input, labels<jupyter_output><empty_output><jupyter_text>**Training testing and evaluating  :**The Neural network architecture that will have 6 hidden layers with one input layer(formed by concatenating drugs and conditions embeddings) and one output layerthe optimizer, learning rate, batch size, epochs ... are decided after doing few experiments, and optimum is chosen for this work.The process includes :1. create a model2. train model3. test/ evaluate model4. calculate HR, NDCG5. check for the best HR, NDCG and save the model6. Repeat  the steps 3, 4 and 5 for "epochs" timesThis will save the best model<jupyter_code>if __name__ == '__main__':

    path = '/content'
    layers = [256,128,64,32,16,8]
    reg_layers = [0,0,0,0,0,0]
    num_negatives =  6
    learner =  'adam'
    learning_rate = 0.001
    batch_size = 256
    epochs = 10
    verbose = 1
    
    topK = 5
    model_out_file = 'Pretrain_new.h5'  
    
    # Loading data
    t1 = time()
    train,test = get_data()

    num_medical_conditions, num_drugs = train.shape
    
    # Build model
    model = get_model(num_medical_conditions, num_drugs, layers, reg_layers)


    #compile model
    model.compile(optimizer=Adam(lr=learning_rate), loss='binary_crossentropy',metrics=['accuracy'])


    
    # Check Init performance
    t1 = time()
    (hr, ndcg) = evaluate(model,topK)
    HR, NDCG = np.array(hr).mean(), np.array(ndcg).mean()
    print('Init: HR = %.4f, NDCG = %.4f [%.1f]' %(HR, NDCG, time()-t1))
    


    # Train model
    best_hr, best_ndcg, best_iter = HR, NDCG, -1


    for epoch in range(epochs):
        t1 = time()

        # Generate training instances
        medical_condition_input, drug_input, labels = get_train_instances(train, num_negatives)
    

        # Training        
        hist = model.fit([np.array(medical_condition_input), np.array(drug_input)],
                         np.array(labels),batch_size=batch_size, nb_epoch=20, verbose=0, shuffle=True)
        '''plt.plot(history.history["loss"])
        #plt.plot(history.history["val_loss"])
        plt.title("model loss")
        plt.ylabel("loss")
        plt.xlabel("epoch")
        plt.legend(["train", "test"], loc="upper left")
        plt.show()'''
        
        t2 = time()

        # Evaluation
        if epoch %verbose == 0:

            (hr, ndcg) = evaluate(model,topK)
            HR, NDCG, loss = np.array(hr).mean(), np.array(ndcg).mean(), hist.history['loss'][0]
            print('Iteration %d [%.1f s]: HR = %.4f, NDCG = %.4f, loss = %.4f [%.1f s]' 
                  % (epoch,  t2-t1, HR, NDCG, loss, time()-t2))
            
            if HR >= best_hr and NDCG >= best_ndcg:

                best_hr, best_ndcg, best_iter = HR, NDCG, epoch
                model.save(model_out_file)



    print("End. Best Iteration %d:  HR = %.4f, NDCG = %.4f. " %(best_iter, best_hr, best_ndcg))
    print("The best MLP model is saved to %s" %(model_out_file))
Best_model = tf.keras.models.load_model(
    '/content/Pretrain_new.h5', compile = True
)<jupyter_output><empty_output><jupyter_text>Now let’s try to find out the possible drugs for Rheumatoid Arthritis( Rheumatoid arthritis (RA) is an autoimmune disease that can cause joint pain and damage throughout your body)<jupyter_code>drugs = [i for i in range(3436)]

medical_conditions = np.full(len(drugs),18, dtype = 'int32')

predictions = Best_model.predict([medical_conditions, np.array(drugs)], 
                                 batch_size=100, verbose=0)

map_drug_score ={}

for i in range(len(drugs)):                                           # creating the{ item : chance } dict 
        drug = drugs[i]
        map_drug_score[drug] = predictions[i]

ranklist = heapq.nlargest(5, map_drug_score, key=map_drug_score.get)



print( str(Medical_Conditions_ID_to_name[18]) +" can be treated by : " )

print("\n")
for i in ranklist:
  print("\n\t"+str(Drugs_ID_to_name[i]))<jupyter_output><empty_output><jupyter_text>The model has predicted a few correct drugs like:* **Hydroxychloroquine**and also suggested possible drugs for the treatment of Rheumatoid arthritis* Etanercept* Acetaminophen / hydrocodone* Naproxen* AleveAlthough the working of these drugs can only be confirmed by the **scientists working in these fields**.  This was the basic model for drug prediction Modification can be done to lear some more complex relations   Few modifications :   *   We can consider the featchers of the drug ( like protine structure , activity in different environment ... )*   large data set can also help to increse the radius of the possibilities and can lead to better predictions * using more deep NN structure * fine tuning the hyperparameters * ...In the second part we will try to create such model and find the possibilites for COVID-19 ...  :)<jupyter_code><jupyter_output><empty_output><jupyter_text>**References **: [1] <NAME>, <NAME>, <NAME>, <NAME>, <NAME> and <NAME> (2017). Neural Collaborative Filtering. In Proceedings of WWW '17, Perth, Australia, April 03-07, 2017.<jupyter_code><jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0941954266998241, 'fraction_numerical': 0.014011039000424576, 'mean_word_length': 2.9932186970210704, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 58, 'https://': 3, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 33, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '12', 'dir': 'jupyter-structured-clean-dedup', 'id': '289127', 'license': 'MIT', 'n_tokens_mistral': 5161, 'n_tokens_neox': 4823, 'n_words': 1481, 'path': 'src/Complete_code.ipynb', 'repo_name': 'mankar1257/Drug-Prediction-using-neural-collaborative-filtering', 'size': '16514'}