{"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":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-06-10T15:06:05.76147Z","iopub.execute_input":"2021-06-10T15:06:05.761916Z","iopub.status.idle":"2021-06-10T15:06:05.775741Z","shell.execute_reply.started":"2021-06-10T15:06:05.761796Z","shell.execute_reply":"2021-06-10T15:06:05.774331Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# BÀI TOÁN ĐẶT RA\n\n    Tìm và lọc ra các câu hỏi được xem là không phù hợp trên Quora\n    - Input: Các câu hỏi kèm với mã ID ngẫu nhiên\n    - Output: 0 (No) và 1 (Yes)\n    \n    ","metadata":{}},{"cell_type":"markdown","source":"# THỰC HIỆN BÀI TOÁN\n1. Xử lý dữ liệu\n- Loại bỏ số, dấu câu và stopword\n- Clean lại câu, sửa lại nghĩa \n2. Training dữ liệu\n- Sử dụng mô hình Logistic Regression ","metadata":{}},{"cell_type":"markdown","source":"# **1, Xử lý dữ liệu:** \nỞ bước này công việc là xử lý từ ngữ của các câu hỏi như là bỏ dấu câu, bỏ số,... sau đó sửa lại câu từ để câu không bị quá khó hiểu","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\n","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:06:05.784198Z","iopub.execute_input":"2021-06-10T15:06:05.784576Z","iopub.status.idle":"2021-06-10T15:06:06.519279Z","shell.execute_reply.started":"2021-06-10T15:06:05.78454Z","shell.execute_reply":"2021-06-10T15:06:06.518102Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd \nimport seaborn as sns","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Tổng quan về dữ liệu**\n- Bộ dữ liệu gồm 1,3 triệu dòng và 3 cột.\n- Các cột bao gồm: qid - unique question identifier (ID riêng của từng câu hỏi), question_text - nội dung của câu hỏi, và target - nếu câu hỏi hợp chuẩn sẽ được đánh 0 và không đạt chuẩn sẽ được đánh 1.\n- Bộ dữ liệu có độ chênh lệch giữa câu hợp chuẩn và không hợp chuẩn rất là lớn.","metadata":{}},{"cell_type":"code","source":"# Sử dụng df của pandas để xử lý giữ liệu cột và hàng\ndf_train = pd.read_csv('/kaggle/input/quora-insincere-questions-classification/train.csv')\ndf_test = pd.read_csv('/kaggle/input/quora-insincere-questions-classification/test.csv')\ndf_sample_sub = pd.read_csv('/kaggle/input/quora-insincere-questions-classification/sample_submission.csv')\n","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:06:06.521291Z","iopub.execute_input":"2021-06-10T15:06:06.521726Z","iopub.status.idle":"2021-06-10T15:06:12.232667Z","shell.execute_reply.started":"2021-06-10T15:06:06.521679Z","shell.execute_reply":"2021-06-10T15:06:12.231607Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#in ra 20 dòng đầu của  bộ dữ liệu sample_sub\ndf_sample_sub.head(20) ","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:06:12.234747Z","iopub.execute_input":"2021-06-10T15:06:12.235166Z","iopub.status.idle":"2021-06-10T15:06:12.261122Z","shell.execute_reply.started":"2021-06-10T15:06:12.235122Z","shell.execute_reply":"2021-06-10T15:06:12.259811Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#in ra 20 dòng đầu của bộ dữ liệu train\ndf_train.head(20) ","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:06:12.263386Z","iopub.execute_input":"2021-06-10T15:06:12.264029Z","iopub.status.idle":"2021-06-10T15:06:12.278692Z","shell.execute_reply.started":"2021-06-10T15:06:12.263976Z","shell.execute_reply":"2021-06-10T15:06:12.277625Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#in ra 20 dòng đầu của bộ dữ liệu test\ndf_test.head(20) ","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:06:12.280537Z","iopub.execute_input":"2021-06-10T15:06:12.281298Z","iopub.status.idle":"2021-06-10T15:06:12.297089Z","shell.execute_reply.started":"2021-06-10T15:06:12.281246Z","shell.execute_reply":"2021-06-10T15:06:12.295945Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Dữ liệu vào là ngôn ngữ tự nhiên. Nếu để nguyên, có vẻ vô cùng khó train và tốn tài nguyên. Yêu cầu đặt ra là cần xử lý các thành phần của câu như dấu câu, số, stopword, và đưa các từ được chia về dạng cơ bản.","metadata":{}},{"cell_type":"code","source":"# Kiểm tra xem trong bộ câu hỏi có trường nào trống không của dataframe \"df_test\"\ndf_train.info()","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:06:12.299094Z","iopub.execute_input":"2021-06-10T15:06:12.29951Z","iopub.status.idle":"2021-06-10T15:06:12.558323Z","shell.execute_reply.started":"2021-06-10T15:06:12.299464Z","shell.execute_reply":"2021-06-10T15:06:12.556354Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Kiểm tra số lượng những câu phù hợp với giá trị là 0 và không phù hợp với giá trị là 1\ndf_train.target.value_counts()","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:06:12.560015Z","iopub.execute_input":"2021-06-10T15:06:12.560445Z","iopub.status.idle":"2021-06-10T15:06:12.582031Z","shell.execute_reply.started":"2021-06-10T15:06:12.560399Z","shell.execute_reply":"2021-06-10T15:06:12.580843Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Vẽ đồ thị liệt kê số lượng hai loại câu\nimport matplotlib.ticker as ticker\n\nncount = df_train.shape[0]\n\nplt.figure(figsize=(7, 5))\n\nax = sns.countplot(data=df_train, x='target')\nplt.title('Portion of Questions')\nplt.xlabel('Number of Axles')\n\n# Tạo 2 trục\nax2=ax.twinx()\n\n# Trục ghi số lượng ở bên phải, phần trăm ở bên trái\nax2.yaxis.tick_left()\nax.yaxis.tick_right()\n\n\nax.yaxis.set_label_position('right')\nax2.yaxis.set_label_position('left')\n\nax2.set_ylabel('Frequency [%]')\n\nfor p in ax.patches:\n    x=p.get_bbox().get_points()[:,0]\n    y=p.get_bbox().get_points()[1,1]\n    ax.annotate('{:.1f}%'.format(100.*y/ncount), (x.mean(), y), \n            ha='center', va='bottom') # set the alignment of the text\n\n# Sử dụng LinearLocator để đảm bảo khoảng cách giữa các mức chính xác cho trục số lượng\nax.yaxis.set_major_locator(ticker.LinearLocator(11))\n\n# Đặt giới hạn phần trăm từ 0 đến 100\nax2.set_ylim(0,100)\nax.set_ylim(0,ncount)\n\n# Sử dụng MultipleLocator để các mức của trục phần trăm cách nhau 10 đơn vị\nax2.yaxis.set_major_locator(ticker.MultipleLocator(10))\n\nax2.grid(None)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:06:12.585508Z","iopub.execute_input":"2021-06-10T15:06:12.585913Z","iopub.status.idle":"2021-06-10T15:06:12.969451Z","shell.execute_reply.started":"2021-06-10T15:06:12.585878Z","shell.execute_reply":"2021-06-10T15:06:12.968266Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Xử lý dữ liệu**\nKhi xử lý một câu tiếng Anh, vấn đề đầu tiên cần làm và chia tách các từ thành các thành phần riêng biệt. Ngoài ra còn hai vấn đề nữa: một là trong một từ tiếng anh có thể có nhiều dạng chia động từ khác nhau như \"drive\", \"drives, \"driving\"; và hai là stopword, là những từ như : “a, a, a, of, or, many vv …”, các từ này không hề có liên quan gì đến nội dung và ý nghĩa của câu. Stopword có thể gây ra rất nhiều noise đến việc xử lý từ.\nNên cần một platform xử lý ngôn ngữ tự nhiên đủ mạnh và có đủ các chức năng. Natural Language Toolkit (NLTK) được lựa chọn do có sẵn nhiều thư viện xử lý từ ngữ cho bài toán phân loại. Trong đó tập trung vào 3 phần chính: Word Tokenize, Stopwords và Lemmatizer để xử lý câu hỏi.\n* Word Tokenize (Phân đoạn từ): được xử dụng để chia tách các từ thành các thành phần riêng biệt nhằm để có thể nhận diện chính xác từng từ hơn. VD như câu \"Chess is one of the oldest known board games.\" sẽ được xử lý thành ['Chess', 'is', 'one', 'of', 'the', 'oldest', 'known', 'board', 'games', '.']  \n* Stopwords: như đã nói ở trên, stopword thường sẽ không liên quan đến nghĩa của câu, để lại sẽ gây ra rất nhiều noise dẫn tới kết quả có sai số lớn. Nên việc cần làm là loại bỏ nó, và NLTK đã có sẵn một list các stopword, có thể lấy ra từ package *nltk.corpus*.\n* Lemmatizer: được xử dụng để đưa các từ được chia ra các dạng về dang cơ bản nhất của nó như là \"seen\", \"saw\", \"seeing\" sẽ được chuyển về \"see\". Việc này cũng sẽ làm giảm noise và giảm tài nguyên, thời gian cần được sử dụng để sử lý. NLTK có sẵn công cụ WordNetLemmatizer cho việc này, có thể lấy ra từ package *nltk.stem*.","metadata":{}},{"cell_type":"code","source":"import nltk\nimport string\nfrom nltk.tokenize import word_tokenize\nfrom nltk.corpus import stopwords\nfrom nltk.stem import WordNetLemmatizer\n\nnltk.download('stopwords')\nnltk_stopwords = stopwords.words('english')\n\nwordnet_lemmatizer = WordNetLemmatizer()\n\n# Xây dựng Lemmatizer để đưa các từ về dạng cơ bản\ndef lemSentence(sentence):\n    token_words = word_tokenize(sentence) #tách câu thành từng từ\n    lem_sentence = []\n    \n    for word in token_words:\n        lem_sentence.append(wordnet_lemmatizer.lemmatize(word, pos=\"v\")) # Sử dụng \"append\" để ghi đè các từ được đưa về dạng cơ bản\n        lem_sentence.append(\" \") # Thêm khoảng trống giữa mỗi từ\n        \n    return \"\".join(lem_sentence) \n\n# Làm \"sạch\" câu bằng loại bỏ dấu câu, số và stopword; đồng thời đưa các từ về dạng cơ bản\ndef clean(message, lem=True):\n    # Bỏ dấu câu\n    message = message.translate(str.maketrans('', '', string.punctuation))\n    \n    # Bỏ số\n    message = message.translate(str.maketrans('', '', string.digits))\n    \n    # Bỏ stop word\n    message = [word for word in word_tokenize(message) if not word.lower() in nltk_stopwords]\n    message = ' '.join(message)\n    \n    # Lemmatazation \n    if lem:\n        message = lemSentence(message)\n    \n    return message","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:06:12.971696Z","iopub.execute_input":"2021-06-10T15:06:12.972142Z","iopub.status.idle":"2021-06-10T15:06:14.014958Z","shell.execute_reply.started":"2021-06-10T15:06:12.972096Z","shell.execute_reply":"2021-06-10T15:06:14.013919Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Làm sạch câu hỏi với hàm lambda: với mỗi x hãy thực hiện \"clean\"\ndf_train['question_text_cleaned'] = df_train.question_text.apply(lambda x: clean(x, True))","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:06:14.016556Z","iopub.execute_input":"2021-06-10T15:06:14.016975Z","iopub.status.idle":"2021-06-10T15:14:55.712429Z","shell.execute_reply.started":"2021-06-10T15:06:14.01693Z","shell.execute_reply":"2021-06-10T15:14:55.711407Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Các câu hỏi sau khi đã được xử lý**\n","metadata":{}},{"cell_type":"code","source":"df_train.head(20)\n","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:14:55.713816Z","iopub.execute_input":"2021-06-10T15:14:55.714235Z","iopub.status.idle":"2021-06-10T15:14:55.729917Z","shell.execute_reply.started":"2021-06-10T15:14:55.714182Z","shell.execute_reply":"2021-06-10T15:14:55.728935Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **2, Training dữ liệu**\n- Với yêu cầu đặt ra, Logistic Regression được lựa chọn vì đây là 1 thuật toán phân loại được dùng để gán các đối tượng cho 1 tập hợp giá trị rời rạc (như 0, 1, 2, ...), và hay được ứng dụng để lọc xem email có phải là spam hay không, có tính tương đồng với bài toán lọc câu hỏi toxic. Nhưng điểm hạn chế của thuật toán này là nó nhạy cảm với noise, điều này đã được xử lý khi ta đưa từ về dạng cơ bản, bỏ stopword, dấu câu và số ở bước trên.\n- F1 Score cũng được sử dụng do đâ bộ dữ liệu có qua nhiều mẫu negative thì Accuracy sẽ không còn chính xác nữa.","metadata":{}},{"cell_type":"markdown","source":"**----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------**","metadata":{}},{"cell_type":"markdown","source":"* Mặc dù đã xử lý dữ liệu, để sử dụng dữ liệu văn bản cho mô hình dự đoán, văn bản phải được phân tích thành dạng vector. Scikit-learning CountVectorizer được sử dụng để chuyển đổi một bộ các tài liệu văn bản thành một vectơ. Nó cũng cho phép xử lý trước dữ liệu văn bản trước khi tạo biểu diễn vector. Ngoài ra nó cũng cung cấp một cách đơn giản để mã hóa bộ các tài liệu văn bản và xây dựng một kho từ vựng của các từ có sẵn. Vectơ được mã hóa sẽ trả lại độ dài của tự vựng và số lần xuất hiện mỗi từ trong văn bản.\n* Pipeline được sử dụng để xâu chuỗi nhiều công cụ đo thành một. Điều này rất hữu ích vì thường có một chuỗi các bước cố định trong quá trình xử lý dữ liệu, đặc biệt là phân loại.","metadata":{}},{"cell_type":"code","source":"\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.metrics import accuracy_score, f1_score\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.feature_extraction.text import CountVectorizer\n\n# Tạo hàm chuyển đổi với CountVectorizer\ncount_vectorizer = CountVectorizer()\nmodel = LogisticRegression(C=1, random_state=0)\n\n# Tạo Pipline với 2 bước\nvectorize_model_pipeline = Pipeline([\n    ('count_vectorizer', count_vectorizer),\n    ('model', model)\n])","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:14:55.731371Z","iopub.execute_input":"2021-06-10T15:14:55.731956Z","iopub.status.idle":"2021-06-10T15:14:55.747657Z","shell.execute_reply.started":"2021-06-10T15:14:55.731909Z","shell.execute_reply":"2021-06-10T15:14:55.746542Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Train tập dữ liệu train và test với test_size = 0.3 \nX_train, X_test, y_train, y_test = train_test_split(df_train['question_text_cleaned'], df_train['target'], test_size=0.3)\nvectorize_model_pipeline.fit(X_train, y_train)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:14:55.749297Z","iopub.execute_input":"2021-06-10T15:14:55.749779Z","iopub.status.idle":"2021-06-10T15:15:32.340154Z","shell.execute_reply.started":"2021-06-10T15:14:55.749719Z","shell.execute_reply":"2021-06-10T15:15:32.337646Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions = vectorize_model_pipeline.predict(X_test)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:15:32.341593Z","iopub.execute_input":"2021-06-10T15:15:32.342194Z","iopub.status.idle":"2021-06-10T15:15:37.223546Z","shell.execute_reply.started":"2021-06-10T15:15:32.342145Z","shell.execute_reply":"2021-06-10T15:15:37.221773Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# In ra Accuracy và F1 Score\nprint('Accuracy :', accuracy_score(y_test, predictions))\nprint('F1 score :', accuracy_score(y_test, predictions))","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:15:37.226288Z","iopub.execute_input":"2021-06-10T15:15:37.22671Z","iopub.status.idle":"2021-06-10T15:15:37.288283Z","shell.execute_reply.started":"2021-06-10T15:15:37.226653Z","shell.execute_reply":"2021-06-10T15:15:37.287077Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Classificaion report dùng để đo chất lượng dự đoán của thuật toán phân loại. Báo cáo cho thấy độ chính xác của precision, recall và f1-score\nfrom sklearn.metrics import classification_report\n\nprint(classification_report(y_test, predictions))","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:15:37.289976Z","iopub.execute_input":"2021-06-10T15:15:37.290379Z","iopub.status.idle":"2021-06-10T15:15:37.788356Z","shell.execute_reply.started":"2021-06-10T15:15:37.290334Z","shell.execute_reply":"2021-06-10T15:15:37.787271Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Kết quả:** Sử dụng Logistic Regression cho ra kết quả khá cao khi tìm các câu hợp quy chuẩn, nhưng không được cao lắm với những câu không phù hợp.","metadata":{}},{"cell_type":"code","source":"# Làm sạch câu \ndf_test['question_text_cleaned'] = df_test.question_text.apply(lambda x: clean(x, True))","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:15:37.789781Z","iopub.execute_input":"2021-06-10T15:15:37.790363Z","iopub.status.idle":"2021-06-10T15:18:07.371241Z","shell.execute_reply.started":"2021-06-10T15:15:37.790317Z","shell.execute_reply":"2021-06-10T15:18:07.370208Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_test['prediction'] = vectorize_model_pipeline.predict(df_test['question_text_cleaned'])","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:18:07.372735Z","iopub.execute_input":"2021-06-10T15:18:07.373142Z","iopub.status.idle":"2021-06-10T15:18:11.728079Z","shell.execute_reply.started":"2021-06-10T15:18:07.3731Z","shell.execute_reply":"2021-06-10T15:18:11.727093Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_final = df_test[['qid','question_text_cleaned','prediction']]\ndf_final.set_index('qid', inplace=True)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:18:11.729515Z","iopub.execute_input":"2021-06-10T15:18:11.729972Z","iopub.status.idle":"2021-06-10T15:18:11.790387Z","shell.execute_reply.started":"2021-06-10T15:18:11.729927Z","shell.execute_reply":"2021-06-10T15:18:11.789395Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# In ra 20 kết quả đầu tiên\ndf_final.head(20)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:18:11.791757Z","iopub.execute_input":"2021-06-10T15:18:11.792372Z","iopub.status.idle":"2021-06-10T15:18:11.803426Z","shell.execute_reply.started":"2021-06-10T15:18:11.792324Z","shell.execute_reply":"2021-06-10T15:18:11.802329Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_final.to_csv('submission.csv')","metadata":{"execution":{"iopub.status.busy":"2021-06-10T15:18:11.805057Z","iopub.execute_input":"2021-06-10T15:18:11.805748Z","iopub.status.idle":"2021-06-10T15:18:12.579604Z","shell.execute_reply.started":"2021-06-10T15:18:11.805703Z","shell.execute_reply":"2021-06-10T15:18:12.578563Z"},"trusted":true},"execution_count":null,"outputs":[]}]}