{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# GIỚI THIỆU BÀI TOÁN\n","metadata":{}},{"cell_type":"markdown","source":"Phân loại các câu hỏi trên Quora xem chúng là thiếu chân thành hay không.\n","metadata":{}},{"cell_type":"markdown","source":"Em đã dùng một số mô hình như là Logistic Regression hay SVM để giải quyết bài toán nhưng trong bài toán, kaggle có cung cấp một số công cụ Embeddings. Do đó em quyết định tập trung vào các tool Embeddings và so sánh cũng như nhận xét sự khác biệt giữa chúng.","metadata":{}},{"cell_type":"markdown","source":"# PHÂN TÍCH DỮ LIỆU","metadata":{}},{"cell_type":"markdown","source":"Trước tiên chúng ta import một số thư viện cần thiết","metadata":{}},{"cell_type":"code","source":"# Import các thư viện cần thiết\nimport os\nimport time\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom tqdm import tqdm\nimport math\nfrom sklearn.model_selection import train_test_split\nfrom sklearn import metrics\n\nfrom keras.preprocessing.text import Tokenizer\nfrom keras.preprocessing.sequence import pad_sequences\nfrom keras.layers import Dense, Input, LSTM, Embedding, Dropout, Activation, Conv1D, LSTM, GRU\nfrom keras.layers import Bidirectional, GlobalMaxPool1D\nfrom keras.models import Model\nfrom keras import initializers, regularizers, constraints, optimizers, layers","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:37:50.662088Z","iopub.execute_input":"2021-06-01T07:37:50.662504Z","iopub.status.idle":"2021-06-01T07:37:56.390685Z","shell.execute_reply.started":"2021-06-01T07:37:50.662412Z","shell.execute_reply":"2021-06-01T07:37:56.389747Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ta sẽ xem xét một chút về các tệp input được cung cấp","metadata":{}},{"cell_type":"code","source":"train_df = pd.read_csv(\"../input/quora-insincere-questions-classification/train.csv\")\ntest_df = pd.read_csv(\"../input/quora-insincere-questions-classification/test.csv\")\nprint(\"Train shape : \",train_df.shape)\nprint(\"Test shape : \",test_df.shape)\ntrain_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:38:00.620928Z","iopub.execute_input":"2021-06-01T07:38:00.621278Z","iopub.status.idle":"2021-06-01T07:38:05.244376Z","shell.execute_reply.started":"2021-06-01T07:38:00.621243Z","shell.execute_reply":"2021-06-01T07:38:05.243580Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Tệp input được cho là 2 tệp train và test. Tệp train gồm có 3 cột lần lượt là mã câu hỏi(qid), nội dung câu hỏi(question_text) và target(= 0 nếu như câu hỏi bình thường, = 1 nếu câu hỏi không chân thành). Có tổng cộng 1306122 câu hỏi trong tệp train và 375806 câu hỏi trong tập test. Dễ dàng thấy được dữ liệu dùng để train chỉ cần hai phần chính: question_text và target.","metadata":{}},{"cell_type":"code","source":"test_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:08:31.016259Z","iopub.execute_input":"2021-06-01T05:08:31.016612Z","iopub.status.idle":"2021-06-01T05:08:31.027999Z","shell.execute_reply.started":"2021-06-01T05:08:31.016559Z","shell.execute_reply":"2021-06-01T05:08:31.026837Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Tệp test khá giống tệp train nhưng không có phần target. Đây là những câu hỏi mà ta cần gán nhãn target(0,1) cũng chính là nhiệm vụ của ta cần giải quyết trong bài toán này.","metadata":{}},{"cell_type":"markdown","source":"Xem xét một chút về tính cân bằng của dữ liệu","metadata":{}},{"cell_type":"code","source":"import seaborn as sns\nimport matplotlib.pyplot as plt \ntarget = train_df['target']\ntarget_1 = 0\nfor target_value in target:\n    if target_value == 1:\n        target_1 += 1\nprint(\"Số câu hỏi trong tệp train:\", len(target))\nprint(\"Số câu hỏi được gán nhãn là 1:\", target_1)\nmyLabels = [\"insincere question\", \"sincere question\"]\nmyCounts = [target_1, len(target) - target_1]\nplt.pie(myCounts, labels = myLabels, autopct='%1.1f%%', shadow=True, startangle=90)\nplt.axis('equal')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:08:34.237201Z","iopub.execute_input":"2021-06-01T05:08:34.237542Z","iopub.status.idle":"2021-06-01T05:08:34.620186Z","shell.execute_reply.started":"2021-06-01T05:08:34.237504Z","shell.execute_reply":"2021-06-01T05:08:34.619401Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Chỉ có 6.2% số câu hỏi trong tập train được gán nhãn là không chân thành (output là 1). Dữ liệu cực kỳ mất cân bằng. Có cách nào để giảm thiểu điều này không? Em nghĩ chúng ta có thể bỏ bớt dữ liệu có nhãn bằng 0 đi. Thêm một số từ không có nghĩa hoặc không ảnh hưởng đến các key-word làm cho câu hỏi trở nên không chân thành, từ đó có thể tạo ra thêm nhiều các câu hỏi gán nhãn 1. Với những dữ liệu mất cân bằng như thế này, chúng ta thường quan tâm nhiều hơn đến việc đánh giá một mô hình bằng F1-score hơn là accuracy","metadata":{}},{"cell_type":"markdown","source":"Đây là một bài toán phân loại nhị phân. Một số mô hình ta có thể nghĩ đến là Logistic Regression, SVM, .. nhưng em nghĩ đây là một bài toán về chuỗi nên việc xử lý chuỗi như thế nào trước khi huấn luyện quan trọng hơn là sử dụng mô hình nào. Để kết hợp với Embeddings, em sử dụng GRU model","metadata":{}},{"cell_type":"markdown","source":"# HUẤN LUYỆN DEEP LEARNING VỚI MÔ HÌNH GRU","metadata":{}},{"cell_type":"markdown","source":"Trước tiên, ta xử lý một chút dữ liệu dùng để train","metadata":{}},{"cell_type":"markdown","source":"Chia tệp train thành 2 phần: train và validate. Tệp train sẽ dùng để huấn luyện còn tệp validate sẽ dùng để kiểm tra xem mô hình có tốt không. Điền \"na\" vào các dữ liệu còn thiếu tránh sự mất mát ","metadata":{}},{"cell_type":"code","source":"# lay ra 10% train de lam validate\ntrain_df, val_df = train_test_split(train_df, test_size=0.1, random_state=2021)\n\n# some config values \nembed_size = 300 # Độ dài của mỗi vector từ\nmax_features = 50000 # Số lượng từ tối đa trong từ điển sẽ sử dụng\nmaxlen = 100 # Số lượng từ tối đa trong một câu\n\n# Điền \"na\" vào các dữ liệu còn trống \ntrain_X = train_df[\"question_text\"].fillna(\"_na_\").values\nval_X = val_df[\"question_text\"].fillna(\"_na_\").values\ntest_X = test_df[\"question_text\"].fillna(\"_na_\").values","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:38:26.540588Z","iopub.execute_input":"2021-06-01T07:38:26.540949Z","iopub.status.idle":"2021-06-01T07:38:27.424295Z","shell.execute_reply.started":"2021-06-01T07:38:26.540914Z","shell.execute_reply":"2021-06-01T07:38:27.423398Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Nếu để dữ liệu là các chuỗi thì máy sẽ không hiểu được. Ta sẽ nghĩ đến việc mã hóa mỗi từ thành một số nguyên dương duy nhất. Giả sử một câu có 15 từ thì sẽ được mã hóa là một vector số 15x1. Ta dùng Tokenizer để làm việc này. Nó sẽ mã hóa các từ thành các số nguyên dương duy nhất. Số càng thấp nghĩa là từ đó càng phổ biến trong từ điển. Để đồng nhất dữ liệu ta cũng dùng pad_sequences đảm bảo các câu đều dài 100 từ (Cắt bớt các câu dài hơn 100 từ, điền 0(không đại diện cho từ nào cả) vào cho đủ các câu chưa đủ 100 từ). Khi đó mỗi câu sẽ được đại diện bằng một vector số 100x1","metadata":{}},{"cell_type":"code","source":"# mã hóa từ thành số, câu thành vector số \ntokenizer = Tokenizer(num_words=max_features)\ntokenizer.fit_on_texts(list(train_X))\ntrain_X = tokenizer.texts_to_sequences(train_X)\nval_X = tokenizer.texts_to_sequences(val_X)\ntest_X = tokenizer.texts_to_sequences(test_X)\n\n# Đảm bảo mỗi câu hỏi luôn dài 100 từ\ntrain_X = pad_sequences(train_X, maxlen=maxlen)\nval_X = pad_sequences(val_X, maxlen=maxlen)\ntest_X = pad_sequences(test_X, maxlen=maxlen)","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:08:46.893217Z","iopub.execute_input":"2021-06-01T05:08:46.893537Z","iopub.status.idle":"2021-06-01T05:09:45.258012Z","shell.execute_reply.started":"2021-06-01T05:08:46.893507Z","shell.execute_reply":"2021-06-01T05:09:45.257166Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ta được tập train_X, val_X, test_X là các vector số tương ứng với mỗi câu hỏi trong các tệp. Xem thử câu hỏi mã số 0 sau khi mã hóa thành như thế nào","metadata":{}},{"cell_type":"code","source":"print(train_X[0])","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:10:06.681534Z","iopub.execute_input":"2021-06-01T05:10:06.681883Z","iopub.status.idle":"2021-06-01T05:10:06.691191Z","shell.execute_reply.started":"2021-06-01T05:10:06.681852Z","shell.execute_reply":"2021-06-01T05:10:06.690341Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Lấy cột target của tệp train và tệp val để đem đi huấn luyện","metadata":{}},{"cell_type":"code","source":"## Get the target values\ntrain_y = train_df['target'].values\nval_y = val_df['target'].values","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:10:10.632663Z","iopub.execute_input":"2021-06-01T05:10:10.632982Z","iopub.status.idle":"2021-06-01T05:10:10.637232Z","shell.execute_reply.started":"2021-06-01T05:10:10.632954Z","shell.execute_reply":"2021-06-01T05:10:10.636327Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Xây dựng GRU model","metadata":{}},{"cell_type":"code","source":"inp = Input(shape=(maxlen,))\nx = Embedding(max_features, embed_size)(inp)\nx = Bidirectional(GRU(64, return_sequences=True))(x)\nx = GlobalMaxPool1D()(x)\nx = Dense(16, activation=\"relu\")(x)\nx = Dropout(0.1)(x)\nx = Dense(1, activation=\"sigmoid\")(x)\nmodel = Model(inputs=inp, outputs=x)\nmodel.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])\n\nprint(model.summary())","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:10:14.052317Z","iopub.execute_input":"2021-06-01T05:10:14.052729Z","iopub.status.idle":"2021-06-01T05:10:16.564582Z","shell.execute_reply.started":"2021-06-01T05:10:14.052696Z","shell.execute_reply":"2021-06-01T05:10:16.563695Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Input sẽ là các vector chuỗi tương ứng với các câu hỏi. Một chuỗi sẽ có 100 từ tương ứng với vector 100 chiều. Tầng Embedding sẽ giúp máy học được nghĩa của các từ là gì. Embedding sẽ chuyển mỗi từ thành 1 vector 1x300 thể hiện nghĩa của từ đấy. Nghĩa là một câu sẽ là một vector số 100x300. Tầng Bidirection sẽ giúp máy học được nghĩa của mỗi câu dựa trên thứ tự của các từ trên mạng noron. Sau đó với mỗi đặc trưng trong 128 đặc trưng, tầng global sẽ chọn ra từ có đặc trưng đó tốt nhất. Các tầng còn lại trong mô hình dùng để phân loại.","metadata":{}},{"cell_type":"markdown","source":"Bắt đầu train với tệp train_X train_Y dùng để train. Đưa dữ liệu vào mạng neural network 2 lần. Mỗi lần đưa chia nhỏ ra thành các batch_size là 512 câu. Dữ liệu dùng để kiểm thử là val_X và val_y.","metadata":{}},{"cell_type":"code","source":"model.fit(train_X, train_y, batch_size=512, epochs=2, validation_data=(val_X, val_y))","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:10:29.409192Z","iopub.execute_input":"2021-06-01T05:10:29.409503Z","iopub.status.idle":"2021-06-01T05:30:54.195046Z","shell.execute_reply.started":"2021-06-01T05:10:29.409474Z","shell.execute_reply":"2021-06-01T05:30:54.194236Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Mô hình có vẻ hoạt động khá tốt. Để xem xét một cách chi tiết hơn, tính F1-score của mô hình với các threshold từ 0.1 đến 0.5 ","metadata":{}},{"cell_type":"code","source":"pred_noemb_val_y = model.predict([val_X], batch_size=1024, verbose=1)\nfor thresh in np.arange(0.1, 0.501, 0.01):\n    thresh = np.round(thresh, 2)\n    print(\"F1 score at threshold {0} is {1}\".format(thresh, metrics.f1_score(val_y, (pred_noemb_val_y>thresh).astype(int))))\n    ","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:32:09.513148Z","iopub.execute_input":"2021-06-01T05:32:09.513475Z","iopub.status.idle":"2021-06-01T05:32:15.997828Z","shell.execute_reply.started":"2021-06-01T05:32:09.513439Z","shell.execute_reply":"2021-06-01T05:32:15.996892Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ta thấy F1-score trong khoảng threshold 0.26-0.4 là khá tốt. Em thấy điều này khá hợp lý vì ta cần quan tâm hơn đến việc \"bỏ sót còn hơn nhầm\" nên ta sẽ nên xét threshold < 0.5(gần 0 hơn là 1). Nghĩa là ta thà lỡ bỏ qua việc xác định các câu hỏi thiếu chân thành còn hơn là xác định nhầm một câu hỏi thiếu chân thành là chân thành.","metadata":{}},{"cell_type":"code","source":"del model, inp, x\nimport gc; gc.collect()\ntime.sleep(10)","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:32:32.235177Z","iopub.execute_input":"2021-06-01T05:32:32.235490Z","iopub.status.idle":"2021-06-01T05:32:42.485595Z","shell.execute_reply.started":"2021-06-01T05:32:32.235461Z","shell.execute_reply":"2021-06-01T05:32:42.484741Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Như chúng ta thấy, với mô hình như trên, ở tầng Embedding máy phải tự học nghĩa của các từ. Ta có thể thay đổi hiệu suất mô hình bằng cách sử dụng một số tool Embedding được kaggle cung cấp. Chúng sẽ mã hóa mỗi từ thành một vector số 300 chiều dựa vào ngữ nghĩa của chúng. Các từ giống nhau thì có các vector nhúng tương tự nhau. Thay vì để cho máy phải học, ta sử dụng các tool Embedding được cung cấp từ trước để máy có thể hiểu được nghĩa của các từ. ","metadata":{}},{"cell_type":"markdown","source":"# CẢI THIỆN HIỆU SUẤT MÔ HÌNH VỚI MỘT SỐ TOOL EMBEDDING","metadata":{}},{"cell_type":"markdown","source":"Xem xét các file nhúng được cung cấp. Chúng được để trong một file zip nên ta tiến hành unzip","metadata":{}},{"cell_type":"code","source":"from zipfile import ZipFile\nfile_name = \"../input/quora-insincere-questions-classification/embeddings.zip\"\nwith ZipFile(file_name, 'r') as zip:\n     # printing all the contents of the zip file\n    zip.printdir()\n  \n    # extracting all the files\n    print('Extracting all the files now...')\n    zip.extractall()\n    print('Done!')","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:32:46.563437Z","iopub.execute_input":"2021-06-01T05:32:46.563787Z","iopub.status.idle":"2021-06-01T05:36:25.623999Z","shell.execute_reply.started":"2021-06-01T05:32:46.563757Z","shell.execute_reply":"2021-06-01T05:36:25.623092Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Lưu lại đường dẫn đến các file nhúng","metadata":{}},{"cell_type":"code","source":"glove = '../working/glove.840B.300d/glove.840B.300d.txt'\nparagram =  '../working/paragram_300_sl999/paragram_300_sl999.txt'\nwiki_news = '../working/wiki-news-300d-1M/wiki-news-300d-1M.vec'","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:38:39.989801Z","iopub.execute_input":"2021-06-01T07:38:39.990140Z","iopub.status.idle":"2021-06-01T07:38:39.995752Z","shell.execute_reply.started":"2021-06-01T07:38:39.990110Z","shell.execute_reply":"2021-06-01T07:38:39.994522Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"cài đặt hàm load_embed dùng để load 3 loại nhúng ","metadata":{}},{"cell_type":"code","source":"def load_embed(file):\n    def get_coefs(word,*arr): \n        return word, np.asarray(arr, dtype='float32')\n    \n    if file == wiki_news:\n        embeddings_index = dict(get_coefs(*o.split(\" \")) for o in open(file) if len(o)>100)\n    else:\n        embeddings_index = dict(get_coefs(*o.split(\" \")) for o in open(file, encoding='latin'))\n        \n    return embeddings_index","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:38:44.004890Z","iopub.execute_input":"2021-06-01T07:38:44.005261Z","iopub.status.idle":"2021-06-01T07:38:44.011494Z","shell.execute_reply.started":"2021-06-01T07:38:44.005226Z","shell.execute_reply":"2021-06-01T07:38:44.010280Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ta sẽ load thử file nhúng Glove Embeddings","metadata":{}},{"cell_type":"code","source":"embed_glove = load_embed(glove)","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:42:31.211884Z","iopub.execute_input":"2021-06-01T05:42:31.212192Z","iopub.status.idle":"2021-06-01T05:46:04.479431Z","shell.execute_reply.started":"2021-06-01T05:42:31.212164Z","shell.execute_reply":"2021-06-01T05:46:04.478619Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Sau khi load ta được embed_glove. embed_glove sẽ cung cấp cho chúng ta vector số ứng với mỗi từ. Ví dụ đây là vector số của từ \"the\"","metadata":{}},{"cell_type":"code","source":"print(embed_glove['the'])","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:54:32.517755Z","iopub.execute_input":"2021-06-01T05:54:32.518091Z","iopub.status.idle":"2021-06-01T05:54:32.526919Z","shell.execute_reply.started":"2021-06-01T05:54:32.518059Z","shell.execute_reply":"2021-06-01T05:54:32.525903Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Nhúng glove embeddings vào các từ đã được mã hóa thành số từ trước bằng tokenizer lưu trong word_index. Thay vì lưu vector số tương ứng với các từ, ta lưu vector số ứng với số đã được mã hóa của từ đấy. Ở đây, em tự đặt 1 ra câu hỏi: File nhúng có đảm bảo mã hóa được tất cả các từ có trong mọi câu hỏi không ? Câu trả lời là không. Vậy ta có một giải pháp: với các từ không được nhúng, ta sẽ lấy phân phối chuẩn của vector số các từ đã biết. ","metadata":{}},{"cell_type":"code","source":"# Lấy phân phối chuẩn của các từ đã biết ghi vào mọi từ \nall_embs = np.stack(embed_glove.values())\nemb_mean,emb_std = all_embs.mean(), all_embs.std()\nembed_size = all_embs.shape[1]\nword_index = tokenizer.word_index\nnb_words = min(max_features, len(word_index))\nembedding_matrix = np.random.normal(emb_mean, emb_std, (nb_words, embed_size))\n\n# Replace nếu như từ đó được định nghĩa trong file nhúng\nfor word, i in word_index.items():\n    if i >= max_features: continue\n    embedding_vector = embed_glove.get(word)\n    if embedding_vector is not None: embedding_matrix[i] = embedding_vector","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:54:35.883393Z","iopub.execute_input":"2021-06-01T05:54:35.883756Z","iopub.status.idle":"2021-06-01T05:54:45.671226Z","shell.execute_reply.started":"2021-06-01T05:54:35.883725Z","shell.execute_reply":"2021-06-01T05:54:45.670353Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(word_index['the'])\nprint(embed_glove['the'])\nprint(embedding_matrix[2])","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:54:49.692205Z","iopub.execute_input":"2021-06-01T05:54:49.694913Z","iopub.status.idle":"2021-06-01T05:54:49.717502Z","shell.execute_reply.started":"2021-06-01T05:54:49.694860Z","shell.execute_reply":"2021-06-01T05:54:49.716606Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"2 cách thể hiện vector số của từ \"the\"","metadata":{}},{"cell_type":"markdown","source":"Xây dựng lại model với glove embeddings. Tầng Embedding được thêm weights là ma trận vector số của các từ embedding_matrix\n","metadata":{}},{"cell_type":"code","source":"inp = Input(shape=(maxlen,))\nx = Embedding(max_features, embed_size, weights=[embedding_matrix])(inp)\n# add weights = [embedding_matrix]\nx = Bidirectional(GRU(64, return_sequences=True))(x)\nx = GlobalMaxPool1D()(x)\nx = Dense(16, activation=\"relu\")(x)\nx = Dropout(0.1)(x)\nx = Dense(1, activation=\"sigmoid\")(x)\nmodel = Model(inputs=inp, outputs=x)\nmodel.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:54:55.828493Z","iopub.execute_input":"2021-06-01T05:54:55.828975Z","iopub.status.idle":"2021-06-01T05:54:56.674388Z","shell.execute_reply.started":"2021-06-01T05:54:55.828930Z","shell.execute_reply":"2021-06-01T05:54:56.673580Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Bắt đầu train với GRU model kèm file nhúng Glove Embeddings","metadata":{}},{"cell_type":"code","source":"model.fit(train_X, train_y, batch_size=512, epochs=2, validation_data=(val_X, val_y))","metadata":{"execution":{"iopub.status.busy":"2021-06-01T05:54:59.287227Z","iopub.execute_input":"2021-06-01T05:54:59.287542Z","iopub.status.idle":"2021-06-01T06:15:58.848673Z","shell.execute_reply.started":"2021-06-01T05:54:59.287513Z","shell.execute_reply":"2021-06-01T06:15:58.847880Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Có vẻ tốt hơn so với việc ta không dùng Embeddings. Xem xét F1-score","metadata":{}},{"cell_type":"code","source":"pred_glove_val_y = model.predict([val_X], batch_size=1024, verbose=1)\nfor thresh in np.arange(0.1, 0.501, 0.01):\n    thresh = np.round(thresh, 2)\n    print(\"F1 score at threshold {0} is {1}\".format(thresh, metrics.f1_score(val_y, (pred_glove_val_y>thresh).astype(int))))","metadata":{"execution":{"iopub.status.busy":"2021-06-01T06:21:45.764843Z","iopub.execute_input":"2021-06-01T06:21:45.765173Z","iopub.status.idle":"2021-06-01T06:21:53.106094Z","shell.execute_reply.started":"2021-06-01T06:21:45.765143Z","shell.execute_reply":"2021-06-01T06:21:53.105275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Kết quả rõ ràng tốt hơn so với việc ta không dùng Embeddings. Tiếp tục thử với Wiki News FastText Embeddings\n","metadata":{}},{"cell_type":"code","source":"# Lưu lại predict\npred_glove_test_y = model.predict([test_X], batch_size=1024, verbose=1)","metadata":{"execution":{"iopub.status.busy":"2021-06-01T06:22:04.375083Z","iopub.execute_input":"2021-06-01T06:22:04.375405Z","iopub.status.idle":"2021-06-01T06:22:20.298139Z","shell.execute_reply.started":"2021-06-01T06:22:04.375375Z","shell.execute_reply":"2021-06-01T06:22:20.297344Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del word_index, embed_glove, all_embs, embedding_matrix, model, inp, x\nimport gc; gc.collect()\ntime.sleep(10)","metadata":{"execution":{"iopub.status.busy":"2021-06-01T06:26:31.320979Z","iopub.execute_input":"2021-06-01T06:26:31.321300Z","iopub.status.idle":"2021-06-01T06:26:42.706240Z","shell.execute_reply.started":"2021-06-01T06:26:31.321270Z","shell.execute_reply":"2021-06-01T06:26:42.705400Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Load wiki news fasttext embeddings","metadata":{}},{"cell_type":"code","source":"embed_wiki_news = load_embed(wiki_news)","metadata":{"execution":{"iopub.status.busy":"2021-06-01T06:31:40.177058Z","iopub.execute_input":"2021-06-01T06:31:40.177403Z","iopub.status.idle":"2021-06-01T06:33:16.397667Z","shell.execute_reply.started":"2021-06-01T06:31:40.177373Z","shell.execute_reply":"2021-06-01T06:33:16.396587Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"all_embs = np.stack(embed_wiki_news.values())\nemb_mean,emb_std = all_embs.mean(), all_embs.std()\nembed_size = all_embs.shape[1]\n\nword_index = tokenizer.word_index\nnb_words = min(max_features, len(word_index))\nembedding_matrix = np.random.normal(emb_mean, emb_std, (nb_words, embed_size))\n\nfor word, i in word_index.items():\n    if i >= max_features: continue\n    embedding_vector = embed_wiki_news.get(word)\n    if embedding_vector is not None: embedding_matrix[i] = embedding_vector","metadata":{"execution":{"iopub.status.busy":"2021-06-01T06:34:12.856137Z","iopub.execute_input":"2021-06-01T06:34:12.856556Z","iopub.status.idle":"2021-06-01T06:34:18.262909Z","shell.execute_reply.started":"2021-06-01T06:34:12.856508Z","shell.execute_reply":"2021-06-01T06:34:18.262068Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"inp = Input(shape=(maxlen,))\nx = Embedding(max_features, embed_size, weights=[embedding_matrix])(inp)\n# add weights = [embedding_matrix]\nx = Bidirectional(GRU(64, return_sequences=True))(x)\nx = GlobalMaxPool1D()(x)\nx = Dense(16, activation=\"relu\")(x)\nx = Dropout(0.1)(x)\nx = Dense(1, activation=\"sigmoid\")(x)\nmodel = Model(inputs=inp, outputs=x)\nmodel.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])","metadata":{"execution":{"iopub.status.busy":"2021-06-01T06:34:41.596807Z","iopub.execute_input":"2021-06-01T06:34:41.597139Z","iopub.status.idle":"2021-06-01T06:34:42.203655Z","shell.execute_reply.started":"2021-06-01T06:34:41.597108Z","shell.execute_reply":"2021-06-01T06:34:42.202777Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.fit(train_X, train_y, batch_size=512, epochs=2, validation_data=(val_X, val_y))","metadata":{"execution":{"iopub.status.busy":"2021-06-01T06:34:45.354047Z","iopub.execute_input":"2021-06-01T06:34:45.354381Z","iopub.status.idle":"2021-06-01T06:55:19.715949Z","shell.execute_reply.started":"2021-06-01T06:34:45.354351Z","shell.execute_reply":"2021-06-01T06:55:19.714956Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_wiki_news_val_y = model.predict([val_X], batch_size=1024, verbose=1)\nfor thresh in np.arange(0.1, 0.501, 0.01):\n    thresh = np.round(thresh, 2)\n    print(\"F1 score at threshold {0} is {1}\".format(thresh, metrics.f1_score(val_y, (pred_glove_val_y>thresh).astype(int))))","metadata":{"execution":{"iopub.status.busy":"2021-06-01T06:59:51.050635Z","iopub.execute_input":"2021-06-01T06:59:51.050967Z","iopub.status.idle":"2021-06-01T06:59:58.186069Z","shell.execute_reply.started":"2021-06-01T06:59:51.050936Z","shell.execute_reply":"2021-06-01T06:59:58.185275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Kết quả tốt hơn so với việc không dùng nhúng, xấp xỉ so với việc dùng Glove Embeddings","metadata":{}},{"cell_type":"code","source":"# Lưu lại predict\npred_wiki_news_test_y = model.predict([test_X], batch_size=1024, verbose=1)\n","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:00:02.602332Z","iopub.execute_input":"2021-06-01T07:00:02.602662Z","iopub.status.idle":"2021-06-01T07:00:18.080164Z","shell.execute_reply.started":"2021-06-01T07:00:02.602631Z","shell.execute_reply":"2021-06-01T07:00:18.079293Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del embed_wiki_news, all_embs, embedding_matrix, model, inp, x\nimport gc; gc.collect()\ntime.sleep(10)","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:02:28.519888Z","iopub.execute_input":"2021-06-01T07:02:28.520339Z","iopub.status.idle":"2021-06-01T07:02:39.617134Z","shell.execute_reply.started":"2021-06-01T07:02:28.520300Z","shell.execute_reply":"2021-06-01T07:02:39.616216Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Thử với Paragram Embeddings","metadata":{}},{"cell_type":"code","source":"embed_paragram = load_embed(paragram)","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:02:42.552704Z","iopub.execute_input":"2021-06-01T07:02:42.553043Z","iopub.status.idle":"2021-06-01T07:05:29.982464Z","shell.execute_reply.started":"2021-06-01T07:02:42.553011Z","shell.execute_reply":"2021-06-01T07:05:29.981633Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"all_embs = np.stack(embed_paragram.values())\nemb_mean,emb_std = all_embs.mean(), all_embs.std()\nembed_size = all_embs.shape[1]\n\nword_index = tokenizer.word_index\nnb_words = min(max_features, len(word_index))\nembedding_matrix = np.random.normal(emb_mean, emb_std, (nb_words, embed_size))\n\nfor word, i in word_index.items():\n    if i >= max_features: continue\n    embedding_vector = embed_paragram.get(word)\n    if embedding_vector is not None: embedding_matrix[i] = embedding_vector","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:07:59.141709Z","iopub.execute_input":"2021-06-01T07:07:59.142068Z","iopub.status.idle":"2021-06-01T07:08:07.597108Z","shell.execute_reply.started":"2021-06-01T07:07:59.142039Z","shell.execute_reply":"2021-06-01T07:08:07.596276Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"inp = Input(shape=(maxlen,))\nx = Embedding(max_features, embed_size, weights=[embedding_matrix])(inp)\n# add weights = [embedding_matrix]\nx = Bidirectional(GRU(64, return_sequences=True))(x)\nx = GlobalMaxPool1D()(x)\nx = Dense(16, activation=\"relu\")(x)\nx = Dropout(0.1)(x)\nx = Dense(1, activation=\"sigmoid\")(x)\nmodel = Model(inputs=inp, outputs=x)\nmodel.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:08:10.818563Z","iopub.execute_input":"2021-06-01T07:08:10.818925Z","iopub.status.idle":"2021-06-01T07:08:11.436951Z","shell.execute_reply.started":"2021-06-01T07:08:10.818892Z","shell.execute_reply":"2021-06-01T07:08:11.436099Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.fit(train_X, train_y, batch_size=512, epochs=2, validation_data=(val_X, val_y))","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:08:13.849205Z","iopub.execute_input":"2021-06-01T07:08:13.849544Z","iopub.status.idle":"2021-06-01T07:29:03.365415Z","shell.execute_reply.started":"2021-06-01T07:08:13.849506Z","shell.execute_reply":"2021-06-01T07:29:03.364225Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_paragram_val_y = model.predict([val_X], batch_size=1024, verbose=1)\nfor thresh in np.arange(0.1, 0.501, 0.01):\n    thresh = np.round(thresh, 2)\n    print(\"F1 score at threshold {0} is {1}\".format(thresh, metrics.f1_score(val_y, (pred_glove_val_y>thresh).astype(int))))","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:30:48.978211Z","iopub.execute_input":"2021-06-01T07:30:48.978516Z","iopub.status.idle":"2021-06-01T07:30:56.003487Z","shell.execute_reply.started":"2021-06-01T07:30:48.978486Z","shell.execute_reply":"2021-06-01T07:30:56.002594Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_paragram_test_y = model.predict([test_X], batch_size=1024, verbose=1)","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:31:22.241135Z","iopub.execute_input":"2021-06-01T07:31:22.241459Z","iopub.status.idle":"2021-06-01T07:31:37.169860Z","shell.execute_reply.started":"2021-06-01T07:31:22.241428Z","shell.execute_reply":"2021-06-01T07:31:37.169029Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Kết quả vẫn xấp xỉ với 2 tool nhúng trước. Ta thử kết hợp cả 3 tool nhúng để xem kết quả có cải thiện hay không","metadata":{}},{"cell_type":"code","source":"pred_val_y = 0.33*pred_glove_val_y + 0.33*pred_wiki_news_val_y + 0.34*pred_paragram_val_y \nfor thresh in np.arange(0.1, 0.501, 0.01):\n    thresh = np.round(thresh, 2)\n    print(\"F1 score at threshold {0} is {1}\".format(thresh, metrics.f1_score(val_y, (pred_val_y>thresh).astype(int))))","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:31:40.546385Z","iopub.execute_input":"2021-06-01T07:31:40.546779Z","iopub.status.idle":"2021-06-01T07:31:41.764921Z","shell.execute_reply.started":"2021-06-01T07:31:40.546745Z","shell.execute_reply":"2021-06-01T07:31:41.764156Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Tốt hơn một chút. Ta quyết định lấy kết quả chính là sự kết hợp của 3 model này. Chọn threshold là 0.33 vì nó cho F1-score là tốt nhất.","metadata":{}},{"cell_type":"code","source":"pred_test_y = 0.33*pred_glove_test_y + 0.33*pred_wiki_news_test_y + 0.34*pred_paragram_test_y\npred_test_y = (pred_test_y>0.33).astype(int)\nout_df = pd.DataFrame({\"qid\":test_df[\"qid\"].values})\nout_df['prediction'] = pred_test_y\nout_df.to_csv(\"submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:32:03.369531Z","iopub.execute_input":"2021-06-01T07:32:03.369891Z","iopub.status.idle":"2021-06-01T07:32:04.413930Z","shell.execute_reply.started":"2021-06-01T07:32:03.369861Z","shell.execute_reply":"2021-06-01T07:32:04.413076Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ta được output theo yêu cầu đề bài. prediction tương ứng với mỗi qid câu hỏi.","metadata":{}},{"cell_type":"code","source":"out_df\n","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:32:08.668930Z","iopub.execute_input":"2021-06-01T07:32:08.669245Z","iopub.status.idle":"2021-06-01T07:32:08.680330Z","shell.execute_reply.started":"2021-06-01T07:32:08.669216Z","shell.execute_reply":"2021-06-01T07:32:08.679264Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Xem qua thử tầm 10 câu hỏi không chân thành xem máy dự đoán có chính xác không","metadata":{}},{"cell_type":"code","source":"dem = 0;\nfor id,i in enumerate(pred_test_y):\n    if i == 1:\n        print(test_df['question_text'][id])\n        dem = dem + 1\n        if dem == 10:\n            break","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:32:12.769980Z","iopub.execute_input":"2021-06-01T07:32:12.770333Z","iopub.status.idle":"2021-06-01T07:32:12.781017Z","shell.execute_reply.started":"2021-06-01T07:32:12.770300Z","shell.execute_reply":"2021-06-01T07:32:12.776810Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Có vẻ mô hình hoạt động khá ổn","metadata":{}},{"cell_type":"markdown","source":"# KẾT LUẬN VÀ ĐẶT VẤN ĐỀ","metadata":{}},{"cell_type":"markdown","source":"Như ta đã thấy:\n- Việc sử dụng nhúng tốt hơn là việc không sử dụng\n- 3 loại nhúng về cơ bản là cho hiệu suất giống như nhau, không thay đổi nhiều\n","metadata":{}},{"cell_type":"markdown","source":"Đặt vấn đề: Liệu ta có thể có hiệu suất tốt hơn nữa không?\nTa thấy việc hiệu suất phụ thuộc vào việc nhúng. Nghĩ lại một chút về lúc xây dựng mô hình GRU. Đối với các từ mà chưa xuất hiện trong file nhúng, ta đành phải lấy phân phối chuẩn của các từ đã xuất hiện. Vậy file nhúng chứa bao nhiêu % các từ đã biết rồi trong tổng số tất cả các từ trong các câu hỏi ?","metadata":{}},{"cell_type":"markdown","source":"Kiểm tra một chút về tỉ lệ phủ của các tool nhúng được cung cấp","metadata":{}},{"cell_type":"code","source":"# Tổng hợp tất cả các câu hỏi lại để lấy ra tất cả các từ vựng trong đó\ndf = pd.concat([test_df, train_df])","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:38:58.688957Z","iopub.execute_input":"2021-06-01T07:38:58.689318Z","iopub.status.idle":"2021-06-01T07:38:58.833081Z","shell.execute_reply.started":"2021-06-01T07:38:58.689285Z","shell.execute_reply":"2021-06-01T07:38:58.832146Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# load lại các file nhúng\nembed_glove = load_embed(glove)\nembed_wiki_news = load_embed(wiki_news)\nembed_paragram = load_embed(paragram)","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:39:00.748352Z","iopub.execute_input":"2021-06-01T07:39:00.748762Z","iopub.status.idle":"2021-06-01T07:47:00.058452Z","shell.execute_reply.started":"2021-06-01T07:39:00.748723Z","shell.execute_reply":"2021-06-01T07:47:00.057517Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Xây dựng vocab chứa mọi từ vựng trong mọi câu hỏi và số lần xuất hiện của chúng\ndef build_vocab(texts):\n    sentences = texts.apply(lambda x: x.split()).values\n    vocab = {}\n    for sentence in sentences:\n        for word in sentence:\n            try:\n                vocab[word] += 1\n            except KeyError:\n                vocab[word] = 1\n    return vocab","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:47:06.464402Z","iopub.execute_input":"2021-06-01T07:47:06.464783Z","iopub.status.idle":"2021-06-01T07:47:06.471261Z","shell.execute_reply.started":"2021-06-01T07:47:06.464747Z","shell.execute_reply":"2021-06-01T07:47:06.470123Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"vocab = build_vocab(df['question_text'])\n","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:47:09.573040Z","iopub.execute_input":"2021-06-01T07:47:09.573383Z","iopub.status.idle":"2021-06-01T07:47:19.960701Z","shell.execute_reply.started":"2021-06-01T07:47:09.573348Z","shell.execute_reply":"2021-06-01T07:47:19.959672Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Hàm check độ phủ của các tool nhúng, độ phủ đối với vocab nghĩa là tool nhúng chứa bao nhiêu từ trong số lượng các từ khác nhau, độ phủ đối với all_text nghĩa là tool nhúng chứa bao nhiêu từ trong số tất cả các từ (tính cả số lượng của các từ trùng lặp)","metadata":{}},{"cell_type":"code","source":"import operator \ndef check_coverage(vocab, embeddings_index):\n    known_words = {}\n    unknown_words = {}\n    nb_known_words = 0\n    nb_unknown_words = 0\n    for word in vocab.keys():\n        try:\n            known_words[word] = embeddings_index[word]\n            nb_known_words += vocab[word]\n        except:\n            unknown_words[word] = vocab[word]\n            nb_unknown_words += vocab[word]\n            pass\n\n    print('Found embeddings for {:.2%} of vocab'.format(len(known_words) / len(vocab)))\n    print('Found embeddings for  {:.2%} of all text'.format(nb_known_words / (nb_known_words + nb_unknown_words)))\n    unknown_words = sorted(unknown_words.items(), key=operator.itemgetter(1))[::-1]\n\n    return unknown_words","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:47:23.569315Z","iopub.execute_input":"2021-06-01T07:47:23.569799Z","iopub.status.idle":"2021-06-01T07:47:23.582317Z","shell.execute_reply.started":"2021-06-01T07:47:23.569752Z","shell.execute_reply":"2021-06-01T07:47:23.581487Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Glove : \")\noov_glove = check_coverage(vocab, embed_glove)\nprint(\"Paragram : \")\noov_paragram = check_coverage(vocab, embed_paragram)\nprint(\"Wiki news FastText : \")\noov_fasttext = check_coverage(vocab, embed_wiki_news)","metadata":{"execution":{"iopub.status.busy":"2021-06-01T07:47:26.300532Z","iopub.execute_input":"2021-06-01T07:47:26.300905Z","iopub.status.idle":"2021-06-01T07:47:28.231074Z","shell.execute_reply.started":"2021-06-01T07:47:26.300872Z","shell.execute_reply":"2021-06-01T07:47:28.230171Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Như ta thấy, Glove Embeddings chỉ phủ có 32% số lượng từ khác nhau trong các câu hỏi, cũng như là 88% trong tất cả các từ. Điều này cho thấy sự đa dạng của từ vựng trong Glove Embeddings là không nhiều. Paragram và Wiki news cũng vậy. Em nghĩ nếu ta tìm cách tăng được độ phủ của các tool nhúng, hiệu suất của bài toán cũng sẽ được tăng lên khá đáng kể!","metadata":{}}]}