<jupyter_start><jupyter_text>Select top 50 Genres<jupyter_code>all_genres = nltk.FreqDist(genres_list) 

# create dataframe
all_genres_df = pd.DataFrame({'Genre': list(all_genres.keys()), 
                              'Count': list(all_genres.values())})

top50_genres = all_genres_df.nlargest(columns="Count", n = 50) 
unique_genre_list_top50 = list(top50_genres.Genre)
plt.figure(figsize=(12,15)) 
ax = sns.barplot(data=top50_genres, x= "Count", y = "Genre") 
ax.set(ylabel = 'Count') 
plt.show()<jupyter_output><empty_output><jupyter_text>Create train / test genre list based on top 50 genres<jupyter_code># set training data
y_traindf = pd.DataFrame()
y_traindf['genrelist'] = books_train['genrelist']
y_traindf['genrelist_intop50'] = y_traindf['genrelist'].apply(lambda y: [x for x in y if x in unique_genre_list_top50])

y_traindf['genrelist_intop50_top5']=y_traindf['genrelist_intop50'].apply(lambda x: ['' if len(x) < 1 else x[0],
                                                                          '' if len(x) < 2 else x[1],
                                                                          '' if len(x) < 3 else x[2],
                                                                          '' if len(x) < 4 else x[3],
                                                                          '' if len(x) < 5 else x[4]])

y_traindf.head(3)
# set testing data
y_testdf = pd.DataFrame() 
y_testdf['genrelist'] = books_test['genrelist']
y_testdf['genrelist_intop50'] = y_testdf['genrelist'].apply(lambda y: [x for x in y if x in unique_genre_list_top50])

y_testdf['genrelist_intop50_top5']=y_testdf['genrelist_intop50'].apply(lambda x: ['' if len(x) < 1 else x[0],
                                                                          '' if len(x) < 2 else x[1],
                                                                          '' if len(x) < 3 else x[2],
                                                                          '' if len(x) < 4 else x[3],
                                                                          '' if len(x) < 5 else x[4]])

y_testdf.head(3)<jupyter_output><empty_output><jupyter_text>Binarize genre labels<jupyter_code># Append the 2 dataframe together before doing the MultiLabelBinarizer
listtogetbinary = pd.DataFrame()
listtogetbinary['genrelist_intop50_top5'] = y_traindf['genrelist_intop50_top5']
listtogetbinary = listtogetbinary.append(y_testdf[['genrelist_intop50_top5']]).reset_index(drop=True)
print(listtogetbinary.shape)
from sklearn.preprocessing import MultiLabelBinarizer
multilabel_binarizer = MultiLabelBinarizer()

y = multilabel_binarizer.fit_transform(listtogetbinary['genrelist_intop50_top5'])
genre_class = multilabel_binarizer.classes_

# delete empty string genre ''
y = np.delete(y, 0, 1)    
genre_class = np.delete(genre_class, 0)

print(y.shape)
print(genre_class.shape)
train_length = int(topics_train.shape[0])
test_length_start = int(train_length)

# reorder the genre targets according to its frequency
y_df = pd.DataFrame(y, columns = genre_class)
y_df_reordered = y_df[unique_genre_list_top50].copy()

train_y_processed= y_df_reordered.iloc[:train_length,:]
test_y_processed = y_df_reordered.iloc[train_length:,:]

print("Training data shape:", x_train.shape)
print("Training labels shape:", train_y_processed.shape)
print("Testing data shape:", x_test.shape)
print("Testing labels shape:", test_y_processed.shape)<jupyter_output>Training data shape: (50000, 2000)
Training labels shape: (50000, 50)
Testing data shape: (10000, 2000)
Testing labels shape: (10000, 50)<jupyter_text>Models<jupyter_code># function to get results from predict_proba matrix
def get_results(prediction_proba):
    df = pd.DataFrame(prediction_proba.todense(), columns = unique_genre_list_top50)

    df['top5_pred']=df.apply(lambda x: list(x.nlargest(5).index), axis=1)
    df['top5_actual'] = listtogetbinary['genrelist_intop50_top5'][train_length:].reset_index().drop(columns=['index'])

    df[['top5_pred','top5_actual']]

    df = df[['top5_pred','top5_actual']].copy()
    df['genre_match_count'] = df.apply(lambda x: len(set(x['top5_pred']) & set(x['top5_actual'])), axis=1)

    df['percent_match'] = df.apply(
        lambda x: x['genre_match_count'] / (1 if len(list(filter(lambda y: y != '', x['top5_actual'])))==0 else len(list(filter(lambda y: y != '', x['top5_actual'])))), 
        axis=1)
    
    match_20_per = df[df.percent_match >= 0.2].count()[0] / len(df)
    match_40_per = df[df.percent_match >= 0.4].count()[0] / len(df)
    match_60_per = df[df.percent_match >= 0.6].count()[0] / len(df)
    match_80_per = df[df.percent_match >= 0.8].count()[0] / len(df)
    match_100_per = df[df.percent_match >= 1.0].count()[0] / len(df)

    print("% of books with at least 20% of genre matched:", round(match_20_per*100,2),'%')
    print("% of books with at least 40% of genre matched:",round(match_40_per*100,2),'%')
    print("% of books with at least 60% of genre matched:",round(match_60_per*100,2),'%')
    print("% of books with at least 80% of genre matched:",round(match_80_per*100,2),'%')
    print("% of books with at least 100% of genre matched:",round(match_100_per*100,2),'%')
    
    return df<jupyter_output><empty_output><jupyter_text>Label Powerset<jupyter_code># using Label Powerset
from skmultilearn.problem_transform import LabelPowerset
from sklearn.naive_bayes import GaussianNB

# initialize Label Powerset multi-label classifier
# with a gaussian naive bayes base classifier
classifier_ps = LabelPowerset(GaussianNB())

# train
%time classifier_ps.fit(x_train, train_y_processed.values)

# predict
%time predictions_ps = classifier_ps.predict(x_test)
%time probability_ps = classifier_ps.predict_proba(x_test)

# print("Accuracy score: ",accuracy_score(test_y_processed, predictions_ps.todense()))
# print("f1 score: ",f1_score(test_y_processed, predictions_ps.todense(), average="micro"))
# print("Jaccard score: ",jaccard_score(test_y_processed, predictions_ps.todense(), average="micro"))
# print("Hamming loss score: ",hamming_loss(test_y_processed, predictions_ps.todense()))
ps_results_df = get_results(probability_ps)
# ps_results_df.to_csv('d2v_ps_results_df.csv')
# pickle.dump(classifier_ps, open('d2v_classifier_ps.model', 'wb'))
ps_results_df<jupyter_output>% of books with at least 20% of genre matched: 83.3 %
% of books with at least 40% of genre matched: 59.14 %
% of books with at least 60% of genre matched: 35.58 %
% of books with at least 80% of genre matched: 14.95 %
% of books with at least 100% of genre matched: 4.5 %<jupyter_text>Adapted Algorithm<jupyter_code>from skmultilearn.adapt import MLkNN

classifier_aa = MLkNN(k=20)

# train
%time classifier_aa.fit(x_train, train_y_processed.values)

# predict
%time predictions_aa = classifier_aa.predict(x_test)
%time probability_aa = classifier_aa.predict_proba(x_test)

# print("Accuracy score: ",accuracy_score(test_y_processed.values, predictions_aa.todense()))
# print("f1 score: ",f1_score(test_y_processed.values, predictions_aa.todense(), average="micro"))
# print("Jaccard score: ",jaccard_score(test_y_processed.values, predictions_aa.todense(), average="micro"))
# print("Hamming loss score: ",hamming_loss(test_y_processed.values, predictions_aa.todense()))
aa_results_df = get_results(probability_aa)
# aa_results_df.to_csv('d2v_aa_results_df.csv')
# pickle.dump(classifier_aa, open('d2v_classifier_aa.model', 'wb'))
aa_results_df<jupyter_output>% of books with at least 20% of genre matched: 82.91 %
% of books with at least 40% of genre matched: 43.62 %
% of books with at least 60% of genre matched: 13.15 %
% of books with at least 80% of genre matched: 2.11 %
% of books with at least 100% of genre matched: 0.83 %<jupyter_text>Ensemble<jupyter_code># from sklearn.naive_bayes import GaussianNB
from skmultilearn.ensemble import RakelD

classifier_R1D = RakelD(
    base_classifier=GaussianNB(),
    base_classifier_require_dense=[True, True],
    labelset_size=10
)

%time classifier_R1D.fit(x_train, train_y_processed)
%time prediction_R1D = classifier_R1D.predict(x_test)
%time probability_R1D = classifier_R1D.predict_proba(x_test)

# print("Accuracy score: ",accuracy_score(test_y_processed, prediction_R1D.todense()))
# print("f1 score: ",f1_score(test_y_processed, prediction_R1D.todense(), average="micro"))
# print("Jaccard score: ",jaccard_score(test_y_processed, prediction_R1D.todense(), average="micro"))
# print("Hamming loss score: ",hamming_loss(test_y_processed, prediction_R1D.todense()))
R1D_results_df = get_results(probability_R1D)
# R1D_results_df.to_csv('d2v_R1D_results_df.csv')
# pickle.dump(classifier_R1D, open('d2v_classifier_R1D.model', 'wb'))
R1D_results_df<jupyter_output>% of books with at least 20% of genre matched: 84.99 %
% of books with at least 40% of genre matched: 55.47 %
% of books with at least 60% of genre matched: 24.11 %
% of books with at least 80% of genre matched: 5.74 %
% of books with at least 100% of genre matched: 1.67 %<jupyter_text>Classifier Chains<jupyter_code># using classifier chains
from skmultilearn.problem_transform import ClassifierChain
from sklearn.naive_bayes import GaussianNB

# initialize classifier chains multi-label classifier
# with a gaussian naive bayes base classifier
classifier_cc = ClassifierChain(GaussianNB())

# train
%time classifier_cc.fit(x_train, train_y_processed)

# predict
%time predictions_cc = classifier_cc.predict(x_test)
%time probability_cc = classifier_cc.predict_proba(x_test)

# print("Accuracy score: ",accuracy_score(test_y_processed, predictions_cc.todense()))
# print("f1 score: ",f1_score(test_y_processed, predictions_cc.todense(), average="micro"))
# print("Jaccard score: ",jaccard_score(test_y_processed, predictions_cc.todense(), average="micro"))
# print("Hamming loss score: ",hamming_loss(test_y_processed, predictions_cc.todense()))
cc_results_df = get_results(probability_cc)
# cc_results_df.to_csv('d2v_cc_results_df.csv')
# pickle.dump(classifier_cc, open('d2v_classifier_cc.model', 'wb'))
cc_results_df<jupyter_output>% of books with at least 20% of genre matched: 87.08 %
% of books with at least 40% of genre matched: 64.37 %
% of books with at least 60% of genre matched: 34.78 %
% of books with at least 80% of genre matched: 9.01 %
% of books with at least 100% of genre matched: 2.64 %<jupyter_text>Dummy Classifier<jupyter_code># Copy a results df
dummy_results_df = R1D_results_df[['top5_pred','top5_actual']].copy()

# Get genre set for each book (not ordered)
dummy_results_df['Sets'] = dummy_results_df['top5_actual'].apply(set).astype(str)

# get most common genre set
dummy_results_df[dummy_results_df['Sets'] == dummy_results_df['Sets'].value_counts().idxmax()]
most_common_genre_set = dummy_results_df[dummy_results_df['Sets'] == dummy_results_df['Sets'].value_counts().idxmax()].iloc[0].top5_actual

# set the most common genre set as the pred for all books
dummy_results_df['top5_pred'] = dummy_results_df['top5_pred'].apply(lambda x: most_common_genre_set)

# Match data
dummy_results_df['genre_match_count'] = dummy_results_df.apply(lambda x: len(set(x['top5_pred']) & set(x['top5_actual'])), axis=1)

dummy_results_df['percent_match'] = dummy_results_df.apply(
    lambda x: x['genre_match_count'] / (1 if len(list(filter(lambda y: y != '', x['top5_actual'])))==0 else len(list(filter(lambda y: y != '', x['top5_actual'])))), 
    axis=1)

# Compute results
match_20_per = dummy_results_df[dummy_results_df.percent_match >= 0.2].count()[0] / len(dummy_results_df)
match_40_per = dummy_results_df[dummy_results_df.percent_match >= 0.4].count()[0] / len(dummy_results_df)
match_60_per = dummy_results_df[dummy_results_df.percent_match >= 0.6].count()[0] / len(dummy_results_df)
match_80_per = dummy_results_df[dummy_results_df.percent_match >= 0.8].count()[0] / len(dummy_results_df)
match_100_per = dummy_results_df[dummy_results_df.percent_match >= 1.0].count()[0] / len(dummy_results_df)

print("% of books with at least 20% of genre matched:", round(match_20_per*100,2),'%')
print("% of books with at least 40% of genre matched:",round(match_40_per*100,2),'%')
print("% of books with at least 60% of genre matched:",round(match_60_per*100,2),'%')
print("% of books with at least 80% of genre matched:",round(match_80_per*100,2),'%')
print("% of books with at least 100% of genre matched:",round(match_100_per*100,2),'%')

dummy_results_df = dummy_results_df.drop(columns=['Sets'])
dummy_results_df<jupyter_output>% of books with at least 20% of genre matched: 55.15 %
% of books with at least 40% of genre matched: 30.18 %
% of books with at least 60% of genre matched: 15.14 %
% of books with at least 80% of genre matched: 8.35 %
% of books with at least 100% of genre matched: 3.72 %<jupyter_text>Tuning  Ensemble (SGD Loss = modified_huber)<jupyter_code># from sklearn.naive_bayes import GaussianNB
from skmultilearn.ensemble import RakelD
from sklearn.linear_model import SGDClassifier

classifier_R1D_SGD_MH = RakelD(
    base_classifier=SGDClassifier(max_iter=10000, n_jobs = 8,loss = 'modified_huber'),
    base_classifier_require_dense=[True, True],
    labelset_size=10
)

%time classifier_R1D_SGD_MH.fit(x_train, train_y_processed)
%time prediction_R1D_SGD_MH = classifier_R1D_SGD_MH.predict(x_test)
%time probability_R1D_SGD_MH = classifier_R1D_SGD_MH.predict_proba(x_test)

# print("Accuracy score: ",accuracy_score(test_y_processed, prediction_R1D_SGD_MH.todense()))
# print("f1 score: ",f1_score(test_y_processed, prediction_R1D_SGD_MH.todense(), average="micro"))
# print("Jaccard score: ",jaccard_score(test_y_processed, prediction_R1D_SGD_MH.todense(), average="micro"))
# print("Hamming loss score: ",hamming_loss(test_y_processed, prediction_R1D_SGD_MH.todense()))
R1D_SGD_MH_results_df = get_results(probability_R1D_SGD_MH)
# R1D_SGD_MH_results_df.to_csv('d2v_R1D_SGD_MH_results_df.csv')
# pickle.dump(classifier_R1D_SGD_MH, open('d2v_classifier_R1D_SGD_MH.model', 'wb'))
R1D_SGD_MH_results_df<jupyter_output>% of books with at least 20% of genre matched: 94.34 %
% of books with at least 40% of genre matched: 76.77 %
% of books with at least 60% of genre matched: 48.2 %
% of books with at least 80% of genre matched: 18.74 %
% of books with at least 100% of genre matched: 5.26 %<jupyter_text>Ensemble (SGD Loss = log)<jupyter_code># from sklearn.naive_bayes import GaussianNB
from skmultilearn.ensemble import RakelD
from sklearn.linear_model import SGDClassifier

classifier_R1D_SGD_LOG = RakelD(
    base_classifier=SGDClassifier(max_iter=10000, n_jobs = 8,loss = 'log'),
    base_classifier_require_dense=[True, True],
    labelset_size=10
)

%time classifier_R1D_SGD_LOG.fit(x_train, train_y_processed)
%time prediction_R1D_SGD_LOG = classifier_R1D_SGD_LOG.predict(x_test)
%time probability_R1D_SGD_LOG = classifier_R1D_SGD_LOG.predict_proba(x_test)

# print("Accuracy score: ",accuracy_score(test_y_processed, prediction_R1D_SGD_LOG.todense()))
# print("f1 score: ",f1_score(test_y_processed, prediction_R1D_SGD_LOG.todense(), average="micro"))
# print("Jaccard score: ",jaccard_score(test_y_processed, prediction_R1D_SGD_LOG.todense(), average="micro"))
# print("Hamming loss score: ",hamming_loss(test_y_processed, prediction_R1D_SGD_LOG.todense()))
R1D_SGD_LOG_results_df = get_results(probability_R1D_SGD_LOG)
# R1D_SGD_LOG_results_df.to_csv('d2v_R1D_SGD_LOG_results_df.csv')
# pickle.dump(classifier_R1D_SGD_LOG, open('d2v_classifier_R1D_SGD_LOG.model', 'wb'))
R1D_SGD_LOG_results_df<jupyter_output>% of books with at least 20% of genre matched: 95.66 %
% of books with at least 40% of genre matched: 80.72 %
% of books with at least 60% of genre matched: 51.78 %
% of books with at least 80% of genre matched: 19.96 %
% of books with at least 100% of genre matched: 5.97 %<jupyter_text>Ensemble (RF)<jupyter_code>from skmultilearn.ensemble import RakelD
from sklearn.ensemble import RandomForestClassifier

classifier_R1D_RF1 = RakelD(
    base_classifier=RandomForestClassifier(random_state=0, n_jobs=8, n_estimators=200),
    base_classifier_require_dense=[True, True],
    labelset_size=10
)

%time classifier_R1D_RF1.fit(x_train, train_y_processed)
%time prediction_R1D_RF1 = classifier_R1D_RF1.predict(x_test)
%time probability_R1D_RF1 = classifier_R1D_RF1.predict_proba(x_test)

# print("Accuracy score: ",accuracy_score(test_y_processed, prediction_R1D_RF1.todense()))
# print("f1 score: ",f1_score(test_y_processed, prediction_R1D_RF1.todense(), average="micro"))
# print("Jaccard score: ",jaccard_score(test_y_processed, prediction_R1D_RF1.todense(), average="micro"))
# print("Hamming loss score: ",hamming_loss(test_y_processed, prediction_R1D_RF1.todense()))
R1D_RF1_results_df = get_results(probability_R1D_RF1)
# R1D_RF1_results_df.to_csv('d2v_R1D_RF1_results_df.csv')
# pickle.dump(classifier_R1D_RF1, open('d2v_classifier_R1D_RF1.model', 'wb'))
R1D_RF1_results_df<jupyter_output>% of books with at least 20% of genre matched: 95.99 %
% of books with at least 40% of genre matched: 80.67 %
% of books with at least 60% of genre matched: 49.4 %
% of books with at least 80% of genre matched: 17.79 %
% of books with at least 100% of genre matched: 4.85 %<jupyter_text>Ensemble (RF estimator = 200 , criterion = entropy)<jupyter_code>from skmultilearn.ensemble import RakelD
from sklearn.ensemble import RandomForestClassifier

classifier_R1D_RF2 = RakelD(
    base_classifier=RandomForestClassifier(random_state=0, n_jobs=8, n_estimators=200, criterion = 'entropy'),
    base_classifier_require_dense=[True, True],
    labelset_size=10
)

%time classifier_R1D_RF2.fit(x_train, train_y_processed)
%time prediction_R1D_RF2 = classifier_R1D_RF2.predict(x_test)
%time probability_R1D_RF2 = classifier_R1D_RF2.predict_proba(x_test)

# print("Accuracy score: ",accuracy_score(test_y_processed, prediction_R1D_RF2.todense()))
# print("f1 score: ",f1_score(test_y_processed, prediction_R1D_RF2.todense(), average="micro"))
# print("Jaccard score: ",jaccard_score(test_y_processed, prediction_R1D_RF2.todense(), average="micro"))
# print("Hamming loss score: ",hamming_loss(test_y_processed, prediction_R1D_RF2.todense()))
R1D_RF2_results_df = get_results(probability_R1D_RF2)
# R1D_RF2_results_df.to_csv('d2v_R1D_RF2_results_df.csv')
# pickle.dump(classifier_R1D_RF2, open('d2v_classifier_R1D_RF2.model', 'wb'))
R1D_RF2_results_df<jupyter_output>% of books with at least 20% of genre matched: 95.89 %
% of books with at least 40% of genre matched: 79.8 %
% of books with at least 60% of genre matched: 49.23 %
% of books with at least 80% of genre matched: 18.39 %
% of books with at least 100% of genre matched: 5.3 %<jupyter_text>Ensemble (RF estimator = 300 , criterion = entropy)<jupyter_code>from skmultilearn.ensemble import RakelD
from sklearn.ensemble import RandomForestClassifier

classifier_R1D_RF3 = RakelD(
    base_classifier=RandomForestClassifier(random_state=0, n_jobs=8, n_estimators=300, criterion = 'entropy'),
    base_classifier_require_dense=[True, True],
    labelset_size=10
)

%time classifier_R1D_RF3.fit(x_train, train_y_processed)
%time prediction_R1D_RF3 = classifier_R1D_RF3.predict(x_test)
%time probability_R1D_RF3 = classifier_R1D_RF3.predict_proba(x_test)

# print("Accuracy score: ",accuracy_score(test_y_processed, prediction_R1D_RF3.todense()))
# print("f1 score: ",f1_score(test_y_processed, prediction_R1D_RF3.todense(), average="micro"))
# print("Jaccard score: ",jaccard_score(test_y_processed, prediction_R1D_RF3.todense(), average="micro"))
# print("Hamming loss score: ",hamming_loss(test_y_processed, prediction_R1D_RF3.todense()))
R1D_RF3_results_df = get_results(probability_R1D_RF3)
# R1D_RF3_results_df.to_csv('d2v_R1D_RF3_results_df.csv')
# pickle.dump(classifier_R1D_RF3, open('d2v_classifier_R1D_RF3.model', 'wb'))
R1D_RF3_results_df<jupyter_output>% of books with at least 20% of genre matched: 96.03 %
% of books with at least 40% of genre matched: 80.69 %
% of books with at least 60% of genre matched: 50.64 %
% of books with at least 80% of genre matched: 19.08 %
% of books with at least 100% of genre matched: 5.4 %<jupyter_text>Ensemble (LogisticRegression)<jupyter_code># from sklearn.naive_bayes import GaussianNB
from skmultilearn.ensemble import RakelD
from sklearn.linear_model import LogisticRegression

classifier_R1D_LR = RakelD(
    base_classifier = LogisticRegression(random_state = 0, max_iter = 10000, n_jobs = 8),
    base_classifier_require_dense=[True, True],
    labelset_size=10
)

%time classifier_R1D_LR.fit(x_train, train_y_processed)
%time prediction_R1D_LR = classifier_R1D_LR.predict(x_test)
%time probability_R1D_LR = classifier_R1D_LR.predict_proba(x_test)

# print("Accuracy score: ",accuracy_score(test_y_processed, prediction_R1D_LR.todense()))
# print("f1 score: ",f1_score(test_y_processed, prediction_R1D_LR.todense(), average="micro"))
# print("Jaccard score: ",jaccard_score(test_y_processed, prediction_R1D_LR.todense(), average="micro"))
# print("Hamming loss score: ",hamming_loss(test_y_processed, prediction_R1D_LR.todense()))
R1D_LR_results_df = get_results(probability_R1D_LR)
# R1D_LR_results_df.to_csv('d2v_R1D_LR_results_df.csv')
# pickle.dump(classifier_R1D_LR, open('d2v_classifier_R1D_LR.model', 'wb'))
R1D_LR_results_df<jupyter_output>% of books with at least 20% of genre matched: 95.19 %
% of books with at least 40% of genre matched: 79.97 %
% of books with at least 60% of genre matched: 53.05 %
% of books with at least 80% of genre matched: 23.02 %
% of books with at least 100% of genre matched: 7.1 %<jupyter_text>Ensemble (LogisticRegression: Newton-cg)<jupyter_code># from sklearn.naive_bayes import GaussianNB
from skmultilearn.ensemble import RakelD
from sklearn.linear_model import LogisticRegression

classifier_R1D_LR_N = RakelD(
    base_classifier = LogisticRegression(random_state = 0, max_iter = 10000, n_jobs = 8, solver = 'newton-cg'),
    base_classifier_require_dense=[True, True],
    labelset_size=10
)

%time classifier_R1D_LR_N.fit(x_train, train_y_processed)
%time prediction_R1D_LR_N = classifier_R1D_LR_N.predict(x_test)
%time probability_R1D_LR_N = classifier_R1D_LR_N.predict_proba(x_test)

# print("Accuracy score: ",accuracy_score(test_y_processed, prediction_R1D_LR_N.todense()))
# print("f1 score: ",f1_score(test_y_processed, prediction_R1D_LR_N.todense(), average="micro"))
# print("Jaccard score: ",jaccard_score(test_y_processed, prediction_R1D_LR_N.todense(), average="micro"))
# print("Hamming loss score: ",hamming_loss(test_y_processed, prediction_R1D_LR_N.todense()))
R1D_LR_N_results_df = get_results(probability_R1D_LR_N)
# R1D_LR_N_results_df.to_csv('d2v_R1D_LR_N_results_df.csv')
# pickle.dump(classifier_R1D_LR_N, open('d2v_classifier_R1D_LR_N.model', 'wb'))
R1D_LR_N_results_df<jupyter_output>% of books with at least 20% of genre matched: 95.37 %
% of books with at least 40% of genre matched: 80.19 %
% of books with at least 60% of genre matched: 53.09 %
% of books with at least 80% of genre matched: 22.38 %
% of books with at least 100% of genre matched: 6.42 %<jupyter_text>Classifier Chains (SGD Loss = modified_huber)<jupyter_code># using classifier chains
from skmultilearn.problem_transform import ClassifierChain
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import SGDClassifier

# initialize classifier chains multi-label classifier
# with a gaussian naive bayes base classifier
classifier_cc_SGD_MH = ClassifierChain(SGDClassifier(max_iter=10000, n_jobs = 8, loss = 'modified_huber'))

# train
%time classifier_cc_SGD_MH.fit(x_train, train_y_processed)

# predict
%time predictions_cc_SGD_MH = classifier_cc_SGD_MH.predict(x_test)
%time probability_cc_SGD_MH = classifier_cc_SGD_MH.predict_proba(x_test)

# print("Accuracy score: ",accuracy_score(test_y_processed, predictions_cc_SGD_MH.todense()))
# print("f1 score: ",f1_score(test_y_processed, predictions_cc_SGD_MH.todense(), average="micro"))
# print("Jaccard score: ",jaccard_score(test_y_processed, predictions_cc_SGD_MH.todense(), average="micro"))
# print("Hamming loss score: ",hamming_loss(test_y_processed, predictions_cc_SGD_MH.todense()))
cc_SGD_MH_results_df = get_results(probability_cc_SGD_MH)
# cc_SGD_MH_results_df.to_csv('d2v_cc_SGD_MH_results_df.csv')
# pickle.dump(classifier_cc_SGD_MH, open('d2v_classifier_cc_SGD_MH.model', 'wb'))
cc_SGD_MH_results_df<jupyter_output>% of books with at least 20% of genre matched: 90.01 %
% of books with at least 40% of genre matched: 70.84 %
% of books with at least 60% of genre matched: 45.54 %
% of books with at least 80% of genre matched: 18.66 %
% of books with at least 100% of genre matched: 5.36 %<jupyter_text>Classifier Chains (SGD Loss = log)<jupyter_code># using classifier chains
from skmultilearn.problem_transform import ClassifierChain
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import SGDClassifier

# initialize classifier chains multi-label classifier
# with a gaussian naive bayes base classifier
classifier_cc_SGD_LOG = ClassifierChain(SGDClassifier(max_iter=10000, n_jobs = 8, loss = 'log'))

# train
%time classifier_cc_SGD_LOG.fit(x_train, train_y_processed)

# predict
%time predictions_cc_SGD_LOG = classifier_cc_SGD_LOG.predict(x_test)
%time probability_cc_SGD_LOG = classifier_cc_SGD_LOG.predict_proba(x_test)

# print("Accuracy score: ",accuracy_score(test_y_processed, predictions_cc_SGD_LOG.todense()))
# print("f1 score: ",f1_score(test_y_processed, predictions_cc_SGD_LOG.todense(), average="micro"))
# print("Jaccard score: ",jaccard_score(test_y_processed, predictions_cc_SGD_LOG.todense(), average="micro"))
# print("Hamming loss score: ",hamming_loss(test_y_processed, predictions_cc_SGD_LOG.todense()))
cc_SGD_LOG_results_df = get_results(probability_cc_SGD_LOG)
# cc_SGD_LOG_results_df.to_csv('d2v_cc_SGD_LOG_results_df.csv')
# pickle.dump(classifier_cc_SGD_LOG, open('d2v_classifier_cc_SGD_LOG.model', 'wb'))
cc_SGD_LOG_results_df<jupyter_output>% of books with at least 20% of genre matched: 89.44 %
% of books with at least 40% of genre matched: 70.22 %
% of books with at least 60% of genre matched: 45.32 %
% of books with at least 80% of genre matched: 20.1 %
% of books with at least 100% of genre matched: 6.41 %<jupyter_text>Classifier Chains (LogisticRegression)<jupyter_code># using classifier chains
from skmultilearn.problem_transform import ClassifierChain
from sklearn.linear_model import LogisticRegression

# initialize classifier chains multi-label classifier
classifier_cc_LR = ClassifierChain(LogisticRegression(random_state = 0, max_iter = 10000, n_jobs = 8))

# train
%time classifier_cc_LR.fit(x_train, train_y_processed)

# predict
%time predictions_cc_LR = classifier_cc_LR.predict(x_test)
%time probability_cc_LR = classifier_cc_LR.predict_proba(x_test)

# print("Accuracy score: ",accuracy_score(test_y_processed, predictions_cc_LR.todense()))
# print("f1 score: ",f1_score(test_y_processed, predictions_cc_LR.todense(), average="micro"))
# print("Jaccard score: ",jaccard_score(test_y_processed, predictions_cc_LR.todense(), average="micro"))
# print("Hamming loss score: ",hamming_loss(test_y_processed, predictions_cc_LR.todense()))
cc_LR_results_df = get_results(probability_cc_LR)
# cc_LR_results_df.to_csv('d2v_cc_LR_results_df.csv')
# pickle.dump(classifier_cc_LR, open('d2v_classifier_cc_LR.model', 'wb'))
cc_LR_results_df<jupyter_output>% of books with at least 20% of genre matched: 90.27 %
% of books with at least 40% of genre matched: 72.13 %
% of books with at least 60% of genre matched: 46.44 %
% of books with at least 80% of genre matched: 20.83 %
% of books with at least 100% of genre matched: 6.06 %<jupyter_text>Neural Networks<jupyter_code># function to get results from predict_proba matrix
def get_results_nn(prediction_proba):
    df = pd.DataFrame(prediction_proba, columns = unique_genre_list_top50)

    df['top5_pred']=df.apply(lambda x: list(x.nlargest(5).index), axis=1)
    df['top5_actual'] = listtogetbinary['genrelist_intop50_top5'][train_length:].reset_index().drop(columns=['index'])

    df[['top5_pred','top5_actual']]

    df = df[['top5_pred','top5_actual']].copy()
    df['genre_match_count'] = df.apply(lambda x: len(set(x['top5_pred']) & set(x['top5_actual'])), axis=1)

    df['percent_match'] = df.apply(
        lambda x: x['genre_match_count'] / (1 if len(list(filter(lambda y: y != '', x['top5_actual'])))==0 else len(list(filter(lambda y: y != '', x['top5_actual'])))), 
        axis=1)
    
    match_20_per = df[df.percent_match >= 0.2].count()[0] / len(df)
    match_40_per = df[df.percent_match >= 0.4].count()[0] / len(df)
    match_60_per = df[df.percent_match >= 0.6].count()[0] / len(df)
    match_80_per = df[df.percent_match >= 0.8].count()[0] / len(df)
    match_100_per = df[df.percent_match >= 1.0].count()[0] / len(df)

    print("% of books with at least 20% of genre matched:", round(match_20_per*100,2),'%')
    print("% of books with at least 40% of genre matched:",round(match_40_per*100,2),'%')
    print("% of books with at least 60% of genre matched:",round(match_60_per*100,2),'%')
    print("% of books with at least 80% of genre matched:",round(match_80_per*100,2),'%')
    print("% of books with at least 100% of genre matched:",round(match_100_per*100,2),'%')
    
    return df
from keras import backend as K
def f1(y_true, y_pred):
    def recall(y_true, y_pred):
        """Recall metric.

        Only computes a batch-wise average of recall.

        Computes the recall, a metric for multi-label classification of
        how many relevant items are selected.
        """
        true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
        possible_positives = K.sum(K.round(K.clip(y_true, 0, 1)))
        recall = true_positives / (possible_positives + K.epsilon())
        return recall

    def precision(y_true, y_pred):
        """Precision metric.

        Only computes a batch-wise average of precision.

        Computes the precision, a metric for multi-label classification of
        how many selected items are relevant.
        """
        true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
        predicted_positives = K.sum(K.round(K.clip(y_pred, 0, 1)))
        precision = true_positives / (predicted_positives + K.epsilon())
        return precision
    precision = precision(y_true, y_pred)
    recall = recall(y_true, y_pred)
    return 2*((precision*recall)/(precision+recall+K.epsilon()))<jupyter_output>Using TensorFlow backend.<jupyter_text>Shallow Network<jupyter_code>from keras.layers import Dense, Input, Dropout
from keras.models import Model
from keras.callbacks import EarlyStopping
from keras.callbacks import ModelCheckpoint
from keras.optimizers import Adamax
import random
random.seed(0)
 
# creating model
inputs = Input(shape = (2000,))
dense1 = Dense(200, activation = 'selu')(inputs)
dense1 = Dropout(0.1)(dense1)
dense2 = Dense(500, activation = 'selu')(dense1)
dense2 = Dropout(0.1)(dense2)
dense3 = Dense(200, activation = 'selu')(dense2)

# sigmoid - sum of probabilities do not need to be 1, unlike softmax
output = Dense(50, activation='sigmoid')(dense3)

model = Model(inputs=inputs, outputs=output)

#early stopping
es = EarlyStopping(monitor='val_accuracy',mode='max', verbose=1, patience=3)

#model checkpoint
mc = ModelCheckpoint('best_model_NN_adamax.h5', monitor='val_accuracy', mode='max', verbose=1, save_best_only=True)

# opt = Adamax(learning_rate=0.0001, beta_1=0.7, beta_2=0.999)

model.compile(loss='binary_crossentropy',
              optimizer= 'Adamax', #'Adamax',#rmsprop
              metrics=['accuracy']) 

epochs = 20
hist = model.fit(x_train, train_y_processed, epochs=epochs, validation_split=0.1,callbacks = [mc,es])
predictions = model.predict(x_test) 
results_df = get_results_nn(predictions)
results_df.to_csv('d2v_NN_shallow.csv')
pickle.dump(model, open('d2v_NN_shallow_results_df.model', 'wb'))
results_df<jupyter_output>% of books with at least 20% of genre matched: 92.9 %
% of books with at least 40% of genre matched: 75.65 %
% of books with at least 60% of genre matched: 49.99 %
% of books with at least 80% of genre matched: 22.07 %
% of books with at least 100% of genre matched: 6.59 %<jupyter_text>Deep model<jupyter_code>from keras import optimizers
from keras.layers import Dropout


### Adam
def f1_micro_pred(y_true, y_pred):
    return f1_score(y_true, y_pred, average='micro')

# creating model
inputs = Input(shape = (2000,))
dense1 = Dense(1400, activation = 'relu')(inputs)
dense1 = Dropout(0.1)(dense1)
dense2 = Dense(1400, activation = 'relu')(dense1)
dense2 = Dropout(0.1)(dense2)
dense3 = Dense(1400, activation = 'relu')(dense2)
dense3 = Dropout(0.1)(dense3)
dense4 = Dense(1400, activation = 'relu')(dense3)
dense4 = Dropout(0.1)(dense4)
dense5 = Dense(1400, activation = 'relu')(dense4)
dense5 = Dropout(0.1)(dense5)
dense6 = Dense(1400, activation = 'relu')(dense5)
dense6 = Dropout(0.1)(dense6)
dense7 = Dense(1400, activation = 'relu')(dense6)
dense7 = Dropout(0.1)(dense7)
dense8 = Dense(1400, activation = 'relu')(dense7)

# sigmoid - sum of probabilities do not need to be 1, unlike softmax
output = Dense(50, activation='sigmoid')(dense7)

model = Model(inputs=inputs, outputs=output)

#early stopping
es = EarlyStopping(monitor='val_accuracy',mode='max', verbose=1, patience=2)

#model checkpoint
mc = ModelCheckpoint('best_model_NN_adam.h5', monitor='val_accuracy', mode='max', verbose=1, save_best_only=True)
# opt = optimizers.Adam(learning_rate=0.01, beta_1=0.9, beta_2=0.999, amsgrad=False)

model.compile(loss='binary_crossentropy',
              optimizer='adam',#adam
              metrics=[f1])

epochs = 20
#hist = model.fit(x_train, train_y_processed, epochs=epochs, validation_split=0.1,callbacks = [mc,es])
model.fit(x_train, train_y_processed, epochs=epochs, validation_split=0.1)
predictions = model.predict(x_test) 
results_df = get_results_nn(predictions)
results_df.to_csv('d2v_NN_deep.csv')
pickle.dump(model, open('d2v_NN_deep_results_df.model', 'wb'))
results_df<jupyter_output>% of books with at least 20% of genre matched: 90.84 %
% of books with at least 40% of genre matched: 73.23 %
% of books with at least 60% of genre matched: 49.07 %
% of books with at least 80% of genre matched: 23.6 %
% of books with at least 100% of genre matched: 7.82 %
{'fraction_non_alphanumeric': 0.1212536443148688, 'fraction_numerical': 0.03982507288629738, 'mean_word_length': 5.908559919436052, 'pattern_counts': {'":': 0, '<': 80, '<?xml version=': 0, '>': 85, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '22', 'dir': 'jupyter-structured-clean-dedup', 'id': '257870', 'license': 'MIT', 'n_tokens_mistral': 13553, 'n_tokens_neox': 12411, 'n_words': 2646, 'path': 'code/GenrePrediction(Doc2vec).ipynb', 'repo_name': 'Abigail-Tee/GroupProjectBT5153', 'size': '34300'}