├── .gitignore ├── README.md ├── app.py ├── model_assets ├── model_V0.pkl └── vectorizer_V0.pkl ├── model_dev ├── BaseModel.py ├── data │ └── cyber_data.json └── model_dev.ipynb ├── requirements.txt ├── static ├── screen-shot-ui.png └── troll-guy.png ├── templates └── index.html └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/python,pycharm 3 | # Edit at https://www.gitignore.io/?templates=python,pycharm 4 | 5 | ### PyCharm ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # User-specific stuff 10 | .idea/**/workspace.xml 11 | .idea/**/tasks.xml 12 | .idea/**/usage.statistics.xml 13 | .idea/**/dictionaries 14 | .idea/**/shelf 15 | 16 | # Generated files 17 | .idea/**/contentModel.xml 18 | 19 | # Sensitive or high-churn files 20 | .idea/**/dataSources/ 21 | .idea/**/dataSources.ids 22 | .idea/**/dataSources.local.xml 23 | .idea/**/sqlDataSources.xml 24 | .idea/**/dynamic.xml 25 | .idea/**/uiDesigner.xml 26 | .idea/**/dbnavigator.xml 27 | 28 | # Gradle 29 | .idea/**/gradle.xml 30 | .idea/**/libraries 31 | 32 | # Gradle and Maven with auto-import 33 | # When using Gradle or Maven with auto-import, you should exclude module files, 34 | # since they will be recreated, and may cause churn. Uncomment if using 35 | # auto-import. 36 | # .idea/modules.xml 37 | # .idea/*.iml 38 | # .idea/modules 39 | # *.iml 40 | # *.ipr 41 | 42 | # CMake 43 | cmake-build-*/ 44 | 45 | # Mongo Explorer plugin 46 | .idea/**/mongoSettings.xml 47 | 48 | # File-based project format 49 | *.iws 50 | 51 | # IntelliJ 52 | out/ 53 | 54 | # mpeltonen/sbt-idea plugin 55 | .idea_modules/ 56 | 57 | # JIRA plugin 58 | atlassian-ide-plugin.xml 59 | 60 | # Cursive Clojure plugin 61 | .idea/replstate.xml 62 | 63 | # Crashlytics plugin (for Android Studio and IntelliJ) 64 | com_crashlytics_export_strings.xml 65 | crashlytics.properties 66 | crashlytics-build.properties 67 | fabric.properties 68 | 69 | # Editor-based Rest Client 70 | .idea/httpRequests 71 | 72 | # Android studio 3.1+ serialized cache file 73 | .idea/caches/build_file_checksums.ser 74 | 75 | ### PyCharm Patch ### 76 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 77 | 78 | # *.iml 79 | # modules.xml 80 | # .idea/misc.xml 81 | # *.ipr 82 | 83 | # Sonarlint plugin 84 | .idea/sonarlint 85 | 86 | ### Python ### 87 | # Byte-compiled / optimized / DLL files 88 | __pycache__/ 89 | *.py[cod] 90 | *$py.class 91 | 92 | # C extensions 93 | *.so 94 | 95 | # Distribution / packaging 96 | .Python 97 | build/ 98 | develop-eggs/ 99 | dist/ 100 | downloads/ 101 | eggs/ 102 | .eggs/ 103 | lib/ 104 | lib64/ 105 | parts/ 106 | sdist/ 107 | var/ 108 | wheels/ 109 | pip-wheel-metadata/ 110 | share/python-wheels/ 111 | *.egg-info/ 112 | .installed.cfg 113 | *.egg 114 | MANIFEST 115 | 116 | # PyInstaller 117 | # Usually these files are written by a python script from a template 118 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 119 | *.manifest 120 | *.spec 121 | 122 | # Installer logs 123 | pip-log.txt 124 | pip-delete-this-directory.txt 125 | 126 | # Unit test / coverage reports 127 | htmlcov/ 128 | .tox/ 129 | .nox/ 130 | .coverage 131 | .coverage.* 132 | .cache 133 | nosetests.xml 134 | coverage.xml 135 | *.cover 136 | .hypothesis/ 137 | .pytest_cache/ 138 | 139 | # Translations 140 | *.mo 141 | *.pot 142 | 143 | # Django stuff: 144 | *.log 145 | local_settings.py 146 | db.sqlite3 147 | db.sqlite3-journal 148 | 149 | # Flask stuff: 150 | instance/ 151 | .webassets-cache 152 | 153 | # Scrapy stuff: 154 | .scrapy 155 | 156 | # Sphinx documentation 157 | docs/_build/ 158 | 159 | # PyBuilder 160 | target/ 161 | 162 | # Jupyter Notebook 163 | .ipynb_checkpoints 164 | 165 | # IPython 166 | profile_default/ 167 | ipython_config.py 168 | 169 | # pyenv 170 | .python-version 171 | 172 | # pipenv 173 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 174 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 175 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 176 | # install all needed dependencies. 177 | #Pipfile.lock 178 | 179 | # celery beat schedule file 180 | celerybeat-schedule 181 | 182 | # SageMath parsed files 183 | *.sage.py 184 | 185 | # Environments 186 | .env 187 | .venv 188 | env/ 189 | venv/ 190 | ENV/ 191 | env.bak/ 192 | venv.bak/ 193 | 194 | # Spyder project settings 195 | .spyderproject 196 | .spyproject 197 | 198 | # Rope project settings 199 | .ropeproject 200 | 201 | # mkdocs documentation 202 | /site 203 | 204 | # mypy 205 | .mypy_cache/ 206 | .dmypy.json 207 | dmypy.json 208 | 209 | # Pyre type checker 210 | .pyre/ 211 | 212 | # End of https://www.gitignore.io/api/python,pycharm -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ml-flask-web-app 2 | 3 | This is a web application designed to show the project structure for a machine learning model deployed using flask. This project features a machine learning model that has been trained to detect whether or not an online comment is a `Cyber-Troll` or `Non Cyber-Troll`. This application acts as an interface for a user to submit new queries. The machine learning model was built using various features of scikit learn: 4 | 5 | * Support Vector Machine (SVM) 6 | * Bag-of-Words text representation (BoW) 7 | * Grid Search + Cross Validation 8 | 9 | Each of these components are developed within the project in an offline setting inside `/model_dev`. The SVM and BoW models will still be needed in a production or testing setting in order to be able to predict user-submitted queries, so they can be serialized via python's pickle functionality and stored within the `/model_assets` folder. 10 | 11 | In order to detect whether or not an online comment is from a cyber troll, you can deploy this application locally and submit queries to the machine learning model to recieve predictions through a simple user interface. The model was trained using the 12 | Dataset for Detection of Cyber-Trolls ([see here](https://dataturks.com/projects/abhishek.narayanan/Dataset%20for%20Detection%20of%20Cyber-Trolls/)). This project emphasizes more the development process of creating deploy-friendly machine learning projects, rather than the creating of the predictive model itself. 13 | 14 | The model development notebook is located [here](https://github.com/wgopar/ml-flask-web-app/blob/master/model_dev/model_dev.ipynb). 15 | 16 | You can also find a blog post that accompanies this repo [here](http://www.wmendozagopar.com/creating-and-deploying-a-machine-learning-project-with-flask.html#creating-and-deploying-a-machine-learning-project-with-flask). 17 | 18 | Note that this project is still *in progress* 19 | 20 | ## Installation 21 | 22 | First clone the repo locally. 23 | ~~~bash 24 | git clone https://github.com/wgopar/ml-flask-web-app.git 25 | ~~~ 26 | 27 | Create a new virtual environment in the project directory. 28 | ~~~bash 29 | python3 -m venv ./venv 30 | ~~~ 31 | 32 | Activate the virtual environment. 33 | ~~~bash 34 | source venv/bin/activate 35 | ~~~ 36 | 37 | While in the virtual environment, install required dependencies from `requirements.txt`. 38 | 39 | ~~~bash 40 | pip install -r ./requirements.txt 41 | ~~~ 42 | 43 | Now we can deploy the web application via 44 | ~~~bash 45 | python app.py 46 | ~~~ 47 | 48 | and navigate to `http://127.0.0.1:5000/` to see it live. On this page, a user can then submit text into the text 49 | field and receive predictions from the trained model and determine if the text most likely came from a `Cyber Troll` or 50 | `Non Cyber-Troll`. 51 | 52 | ![Screen shot](/static/screen-shot-ui.png "User Interface") 53 | 54 | 55 | The application may then be terminated with the following commands. 56 | ~~~bash 57 | $ ^C # exit flask application (ctrl-c) 58 | $ deactivate # exit virtual environment 59 | ~~~ 60 | 61 | ## Project Structure 62 | 63 | ~~~ 64 | ml-flask-web-app 65 | ├── model_assets 66 | │ ├── model.pkl 67 | │ └── vectorizer.pkl 68 | ├── model_dev 69 | │ ├── data 70 | │ | └── data.json 71 | │ └── model_dev.ipynb 72 | ├── templates 73 | │ └── index.html 74 | ├── app.py 75 | ├── utils.py 76 | ├── requirements.txt 77 | └── README.md 78 | ~~~ 79 | 80 | ### detailed 81 | 82 | `/model_assets` is used to store persisted states of the predictive model and learned feature extractors from scikit-learn. 83 | 84 | `/model_dev` is used as the model development playground where an `.ipynb` is used to develop the model and save new versions of persisted states. 85 | 86 | Storing new persisted states of the model can be done within the jupyter notebook. As an example, within `model_dev.ipynb` 87 | I can create a new model/retrain and include in into the `./model_assets` folder when I am satisfied. A simple example: 88 | 89 | ~~~~python 90 | import utils 91 | 92 | clf = LogisticRegression() 93 | clf.fit(X_train, y_train) 94 | utils.persist_model(clf, description='clf_v.0.0') # creates clf_v.0.0.pkl in /model_assets folder 95 | ~~~~ 96 | 97 | Selecting the version of models to use during run time is chosen within the POST request function inside 98 | in `app.py`. 99 | 100 | `/templates` holds the html templates for the application. 101 | 102 | 103 | 104 | []: './static/screen-shot-ui.png' 105 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, render_template, jsonify, url_for 2 | from utils import clean_text 3 | import pickle 4 | import time 5 | import os 6 | 7 | app = Flask(__name__) 8 | 9 | MODEL_VERSION = 'model_V0.pkl' 10 | VECTORIZER_VERSION = 'vectorizer_V0.pkl' 11 | 12 | # load model assets 13 | vectorizer_path = os.path.join(os.getcwd(), 'model_assets', VECTORIZER_VERSION) 14 | model_path = os.path.join(os.getcwd(), 'model_assets', MODEL_VERSION) 15 | vectorizer = pickle.load(open(vectorizer_path, 'rb')) 16 | model = pickle.load(open(model_path, 'rb')) 17 | 18 | # TODO: add versioning to url 19 | @app.route('/', methods=['GET', 'POST']) 20 | def predict(): 21 | """ Main webpage with user input through form and prediction displayed 22 | 23 | :return: main webpage host, displays prediction if user submitted in text field 24 | """ 25 | 26 | if request.method == 'POST': 27 | 28 | response = request.form['text'] 29 | input_text = clean_text(response) 30 | input_text = vectorizer.transform([input_text]) 31 | prediction = model.predict(input_text) 32 | prediction = 'Cyber-Troll' if prediction[0] == 1 else 'Non Cyber-Troll' 33 | return render_template('index.html', text=prediction, submission=response) 34 | 35 | if request.method == 'GET': 36 | return render_template('index.html') 37 | 38 | # TODO: add versioning to api 39 | @app.route('/predict', methods=['POST']) 40 | def predict_api(): 41 | """ endpoint for model queries (non gui) 42 | 43 | :return: json, model prediction and response time 44 | """ 45 | start_time = time.time() 46 | 47 | request_data = request.json 48 | input_text = request_data['data'] 49 | input_text = clean_text(input_text) 50 | input_text = vectorizer.transform([input_text]) 51 | prediction = model.predict(input_text) 52 | prediction = 'Cyber-Troll' if prediction[0] == 1 else "Non Cyber-Troll" # post processing 53 | 54 | response = {'prediction': prediction, 'response_time': time.time() - start_time} 55 | return jsonify(response) 56 | 57 | 58 | if __name__ == '__main__': 59 | app.run(debug=True) 60 | -------------------------------------------------------------------------------- /model_assets/model_V0.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgopar/ml-flask-web-app/df90234a2aa2e3b0009292fdc356dfb6a5c05bcb/model_assets/model_V0.pkl -------------------------------------------------------------------------------- /model_assets/vectorizer_V0.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgopar/ml-flask-web-app/df90234a2aa2e3b0009292fdc356dfb6a5c05bcb/model_assets/vectorizer_V0.pkl -------------------------------------------------------------------------------- /model_dev/BaseModel.py: -------------------------------------------------------------------------------- 1 | from sklearn.metrics import confusion_matrix 2 | from sklearn.model_selection import GridSearchCV 3 | from sklearn.svm import LinearSVC 4 | 5 | 6 | class SVM: 7 | """ SVM model to support Online Troll comment detection 8 | 9 | Attributes 10 | ---------- 11 | description : string, model description for referencing parameters of object 12 | 13 | clf : sklearn svm model object 14 | 15 | """ 16 | 17 | def __init__(self, description): 18 | """ Initialize model configuration 19 | 20 | Parameters 21 | -------------- 22 | data: (dict) dictionary of training and testings sets of data 23 | description: (str) description of model being trained 24 | """ 25 | self.description = description 26 | self.clf = LinearSVC() 27 | 28 | def train(self, data, **params): 29 | """ Trains model with grid search over user defined parameters 30 | 31 | Parameters 32 | ------------- 33 | parameters: (dict) key value pairs specifying sklearn parameter search 34 | 35 | """ 36 | 37 | self.clf = GridSearchCV(self.clf, params, cv=5) 38 | self.clf.fit(data['X_train'], data['y_train']) 39 | 40 | def display_results(self, data): 41 | """ Prints testing and training accuracies along with other model 42 | validation metrics. 43 | 44 | Parameters 45 | --------------- 46 | clf: (scikit-learn model) predictive model to test 47 | data: (dict) training and testing data for model 48 | """ 49 | train_accuracy = self.clf.score(data['X_train'], data['y_train']) 50 | test_accuracy = self.clf.score(data['X_test'], data['y_test']) 51 | y_pred = self.clf.predict(data['X_test']) 52 | print('{:>20s} {:.2f}'.format('Train Accuracy:', train_accuracy)) 53 | print('{:>20s} {:.2f}'.format('Test Accuracy:', test_accuracy)) 54 | print(confusion_matrix(data['y_test'], y_pred)) -------------------------------------------------------------------------------- /model_dev/model_dev.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 175, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import os\n", 10 | "import sys\n", 11 | "\n", 12 | "import warnings \n", 13 | "warnings.filterwarnings('ignore')\n", 14 | "\n", 15 | "# include app-wide functions \n", 16 | "sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath('utils.py'))))\n", 17 | "import utils\n", 18 | "\n", 19 | "from sklearn.feature_extraction.text import CountVectorizer\n", 20 | "from sklearn.model_selection import train_test_split\n", 21 | "from collections import Counter\n", 22 | "import matplotlib.pyplot as plt\n", 23 | "from bs4 import BeautifulSoup\n", 24 | "import numpy as np\n", 25 | "import pandas as pd\n", 26 | "import pickle \n", 27 | "import random\n", 28 | "import string\n", 29 | "import json\n", 30 | "\n", 31 | "%config InlineBackend.figure_format = 'retina'\n", 32 | "%matplotlib inline" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 87, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "df = utils.load_data() # cleaned dataset" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": null, 47 | "metadata": {}, 48 | "outputs": [], 49 | "source": [ 50 | "#TODO: only allow words occuring at least 3 times + remove stop words (and or i)" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "metadata": {}, 56 | "source": [ 57 | "## some exploratory data analysis" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 88, 63 | "metadata": {}, 64 | "outputs": [ 65 | { 66 | "data": { 67 | "image/png": "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Iu1jwJPAicB7wCDgE+AK4CMzuyNeJjN7BhgLlANGAhOAc4F3zKx7nPQGvA48CxQEhgKTgQbAXDNrESdPMvAB8AiwHXgOmAW0BL40szpZ7JuIiIj4ihQpwsMPP8z27dvp27dvjvJOnDiRBg0aUKJECQoXLsy5557LgAED4k5YFfQH2LVrF/fddx+nnnoqycnJnHHGGTz11FM5GrP+9ttvZ/v27fTs2ZOePXvGvVWqQoUKTJw4kXr16gFQt25d8ufPz+rVq+Nu85lnnsHMGDRoUIZ127Zto3v37px88skUKlSIs88+m+effz60zp999hmtWrWibNmyFCxYkFNOOYVbb72VX375JUPahg0bYmbs3buXfv36cdZZZ5GcnEyHDh2yfTwARo4cCUDnzp1p27YtGzZsYOrUqaHpf/nlF9q3b0+ZMmUoXLgw1atXZ8KECaH9AC666CIKFCjAnj176NOnD5UqVSI5OZlOnTqlS/fqq6/SsGFDSpYsGTlWTzzxBHv37s1Qhzlz5tC8eXPKly9PcnIyZcuWpV69ejz22GPp0q1fv5577rmHs846i6JFi1KyZEkqV67MTTfdlO79jFf3M888k0KFCrFly5a4x6F///6YGcOHD0+3/KeffqJr166cfvrpJCcnc/zxx9OiRQsWLlwYekwTLbcChruBSkBx4LYs0s4AajjnznHO3eqce9A59y+gMbAPGGhm5aIz+C0C9wI/AOc55+52znUDagKbgWfMLCWmnGuBVsB8oJpz7j7nXEegEXAAGGlmxWLy3ANcCLwJ1HHO9XTOtfW3UwQYbWbqKC4iIpJN3bp1o2LFigwfPpxly5ZlK0+vXr1o06YNixcvpm3btnTv3h3nHL169eKyyy6Le3V73759NGnShLfeeotmzZrRqVMn/vjjDx544AH69euXrXJXrVrFrFmzKFSoEPfff3+W6ZOTkwHo2rUraWlpkRPrWKNGjSI5OZn27dunW753714uvfRS3n//fa699lpuueUWtm7dyp133kn37hmuhTJmzBguvPBCpk+fTqNGjbjrrruoVasWo0aNolatWqxZsyZu+VdffTUvvfQS9evX56677uLcc8/Nct8C69at491336VKlSpccMEF3HTTTQCMGDEibvr169dTr149xo8fT9WqVbnrrrs4//zz6dy5My+++GKmZbVs2ZIRI0Zw4YUX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O+8wY8aMyP5169aNhx56KMv5Qa6++mq6devGK6+8Qv/+/cmXLx9Tpkxh69at9OjRI937G7yHb7zxRqbbjPceJlrCb0kys/zAK3idjd8AnjnETeVkoPDg0sERy+OcqxnvAWTeg0ZEROQY9eSTT5KUlMSDDz4Yen94iRLe9bv169fHXR/cNhSky03BbSJffvllupP17Lrtttsi8xQAkVtobrzxxrjpf/vtt7iBTLDv0fsY/L1t2zacc6GPv//97xm2F6/F5NJLL82Qd//+gzdlbNiwgXfeeQeA1q1bY2bpHlOmTAHSd34uXrx4JG88YcszE+x37dq1M93v2I7wV155JbNnz2br1q3MmjWLO++8k++++47mzZunu33o1FNPZfTo0WzYsIHvv/+e5557jpIlS9KnT590fTDCFC1alFatWrF27Vo++ugjIP7tSNH78u6772a6Lw899FCOj9ORltCAwQ8WJgCtgYlAO5dxHLHgGxv2yxCvZSCrPMVzKY+IiIhkU8WKFenatSurVq3ihRdeiJumevXqAHHv416xYgVr166lQoUKOeqUnF0VKlTg0ksvZffu3VleyQcyDPHavHlzTj31VMaMGcPMmTNZunQp11xzDccdd1zc/Pv372f+/PkZlgf7HhwLINJheN68eF0zc9+YMWPYt28ftWvXpmPHjnEfpUuX5v3334+MznT22WeTnJzMN998w86dOzNsM6v+K/GULFmSs846i++//56tW7fmOH/RokVp3LgxQ4YMoWfPnuzevTvSkhAtX758VK1alTvuuIP3338fgLfffjtbZQQtMOPGjWPDhg3MnDmTGjVqZOhwndfvYW5KWMBgZgWA1/CGP/0P0DZefwPn3E68uR3+Fjvcqu9M/zm678EKvKFTT/fLyU6eINwM66MQL4+IiIjkwCOPPELJkiV5/PHH4956cfPNNwNeB9iNGzdGlh84cIAePXqQlpZGx44dj1j9XnjhBYoXL86AAQMYNGhQuqvugTVr1nDttddG7kkP5MuXj86dO/Prr79G9qNLly4Z8kd78MEH0wUemzdvjoz1H4xMBNC9e3eSkpK4++674w5Pu3fv3lw7EXXORTqWDxs2jFGjRsV9dOrUKdIxGry+BK1bt2bLli2R+/kDX3/9ddyOydlxzz33sHv3bjp27Bi35Wfz5s3p+nvMmTMnbstN0MIRdIZe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unQp+fJ510cff/xx3n//fbp3786QIUPInz8/AAcOHKBz586MHj2aVq1a0aJFiwz78+CDD/LEE09ke38CzjlGjRpF/vz5ueGGGwC46aabeP755xkxYgTXXXdduvTbtm2ja9euFCxYkE8//ZRzzz03sq5Hjx6Z3sI1b948Hn74Yfr165dh3Q033MCaNWuYOHEirVu3jizfsmULDRo0oHv37jRv3pwyZcqwefNm3nzzTerUqcP8+fMjxy+wadOmyN8jR47EOcfcuXMzBIe//fZblsenffv2PPLII7zxxht06dIl3bpx48ZF0gQ++OADHnvsMS666CKmTZtGiRIlIutGjRrFLbfcQr9+/Rg4cGCWZUv2qYVBREREctW6desAKF++fLbzXHXVVZx00kmMHTs23a06W7duZeLEiVSsWJFLL700Q74zzjiDrl27plvWokUL/v73v7NixYpIK0NaWhpDhw6lbNmyPPvss5FgASB//vwMGjQIM+PVV1/NUMaJJ57Io48+mu19iTZ79myWL1/OZZddFrl/v1q1apx//vmkpqayfPnydOknT57M9u3bufHGG9MFC+BdWc+sZeOkk06id+/eGZYvXLiQTz75hDZt2qQLFgCOO+44+vTpw65du5g8eTIAZoZzjuTk5AzBAsDxxx+fYVmRIkUyLCtdunRoXQM33ngjZhYJDgK7d+9m4sSJlCtXjiZNmkSWP//884AXHEQHCwCdOnWiatWqcd9DOTxqYRAREZFc5ZwDvBPP7CpQoACdOnWiX79+vPXWW7Rt2xaAV155hT/++IPOnTvH3d7FF18c96S2YcOGzJkzh6+//pq///3vLFu2jE2bNnHmmWfSv3//uHUoXLgwixcvzrD8/PPPJzk5OcPySZMm8d1336VbVqNGDf75z39GXo8cORLwWhWitW/fnnvuuYeRI0fy9NNPR5Z//fXXAHFHjypevDjnnXdeun4J0apVq0bBggUzLF+wYAHgtSbEm7diw4YNAJF9P+6442jWrBnTp0+nevXq/Otf/+Liiy+mTp06FC5cOF3e66+/nqlTp1KrVi3atGlDo0aNuPDCCzn55JPj1jHWaaedRsOGDSO3o1WqVAnwWom2bt3KLbfcki64W7BgAcnJybz22mtxt7d//37WrVvHtm3bMgQUcugUMIiIiEiuOumkk1iyZEmOJ+jq3LkzTzzx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06axcOFCpk+fTvPmzalTp06iq5UQn3zyCe3ataNJkyakpKSwY8cOFixYwLfffstpp50WmRdBJK8oYBAREZGEW7hwIb169aJ48eK0bt06w8hHfyVVqlTh8ssv55NPPmHatGkcOHCAU045hTvvvJNevXpRunTpRFdR/mIUMIiIiEjCdejQITK05l9dxYoV+c9//pPoaohEqA+DiIiIiIiEUsAgIiIiIiKhFDCIiIiIiPwJJGo6BAUMIiIiucTMm+8mLS0twTURkWNREDAEvzV5RQGDiIhILklOTgZg586dCa6JiByLgt+W4LcmryhgEBERySXFihUDYP369ezYsYO0tLSE3UIgIscG5xxpaWns2LGD9evXAwd/a/KKhlUVERHJJaVKlWLnzp3s2rWLtWvXJro6InIMKlKkCKVKlcrTMhUwiIiI5JJ8+fJxyimnsHnzZnbs2MGePXvUwiAih83MSE5OplixYpQqVYp8+fL2JiEFDCIiIrkoX758lC5dWrPxisgxQ30YREREREQklAIGEREREREJpYBBRERERERCKWAQEREREZFQChhERERERCSUAgYREREREQmlgEFEREREREIpYBARERERkVAKGEREREREJJQCBhERERERCaWAQUREREREQilgEBERERGRUAoYREREREQkVK4EDGbWysxeMLN5ZrbdzJyZTcgiT30ze8/MNpvZLjP7zszuMrP8meRpbmapZrbNzH43s8/MrH0W5bQ3s8/99Nv8/M0zSZ/fr8d3ZvaHX7/3zKx+1kdCREREROTYklstDL2B7kA14OesEptZC2Au0ACYDLwIFASeBV4PydMdeAeoCkwARgInAWPN7JmQPM8AY4FyfvoJwLnAO/72YtObX/6zfn2G+vVrAMz16y0iIiIi8peRWwHD3UAloDhwW2YJzaw43sn7AaChc66jc+4+vGBjAdDKzK6NyZMCPANsBmo557o55+4GzgN+AO41s3oxeeoD9/rrz3PO3e2c6wbU9LfzjL/daNcCrYD5QDXn3H3OuY5AI7++I82sWHYPioiIiIjIn12uBAzOudnOueXOOZeN5K2AMsDrzrkvo7axG6+lAjIGHTcDycBQ59zqqDxbgCf8l11i8gSvH/fTBXlW47VoJAM3xeQJyu3t1yfI8wXwhl/vVlnuoYiIiIjIMSIRnZ4v8Z9nxFk3F9gF1Dez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\n", 68 | "text/plain": [ 69 | "" 70 | ] 71 | }, 72 | "metadata": { 73 | "image/png": { 74 | "height": 261, 75 | "width": 390 76 | }, 77 | "needs_background": "light" 78 | }, 79 | "output_type": "display_data" 80 | } 81 | ], 82 | "source": [ 83 | "# distribution of cyber trolls vs non-cyber trolls\n", 84 | "counter = Counter(df.label)\n", 85 | "plt.title('Distribution of CyberTrolls - Train set')\n", 86 | "plt.bar(list(counter.keys())[0], list(counter.values())[0], align='center', color='g', label='Non Cyber-Agressive')\n", 87 | "plt.bar(list(counter.keys())[1], list(counter.values())[1], align='center', color='r', label='Cyber-Agressive')\n", 88 | "plt.xticks(list(set(df.label)))\n", 89 | "plt.legend()\n", 90 | "plt.show()" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 90, 96 | "metadata": {}, 97 | "outputs": [ 98 | { 99 | "name": "stdout", 100 | "output_type": "stream", 101 | "text": [ 102 | "Label: 0\n", 103 | "Index: 788\tuh well suck complainjk\n", 104 | "\n", 105 | "Label: 0\n", 106 | "Index: 9969\tmmm\n", 107 | "\n", 108 | "Label: 0\n", 109 | "Index: 5062\tdamn trying number list dont need more p\n", 110 | "\n", 111 | "Label: 0\n", 112 | "Index: 1167\tson cant decide cuz blackberry sooo delish oh yea ill cd find damn blank cd lol\n", 113 | "\n", 114 | "Label: 0\n", 115 | "Index: 9286\tdo you go wit anybody at this moment\n", 116 | "\n", 117 | "Label: 0\n", 118 | "Index: 8664\ti would love follow r absolutely\n", 119 | "\n", 120 | "Label: 0\n", 121 | "Index: 11964\tany new exciting news youud like share\n", 122 | "\n", 123 | "Label: 0\n", 124 | "Index: 10774\thahhahah yeah you\n", 125 | "\n", 126 | "Label: 0\n", 127 | "Index: 190\toh man funny tweet im tear here hahaha i hate mac genuises soooo much\n", 128 | "\n", 129 | "Label: 0\n", 130 | "Index: 3028\thi best fucking friend reply me jaja i love you motherfucker mua or cku\n", 131 | "\n", 132 | "Label: 1\n", 133 | "Index: 3108\tknow bambis as nice place lol\n", 134 | "\n", 135 | "Label: 1\n", 136 | "Index: 951\ti nominate jcroft shorty award downassbitches totally one\n", 137 | "\n", 138 | "Label: 1\n", 139 | "Index: 7185\ttotally the as end jeep life form impacted\n", 140 | "\n", 141 | "Label: 1\n", 142 | "Index: 7085\tnerd\n", 143 | "\n", 144 | "Label: 1\n", 145 | "Index: 7620\tlmao i seen dude mad angry got as beat\n", 146 | "\n", 147 | "Label: 1\n", 148 | "Index: 3562\ti hate wedding u shud grateful male worrying u got wear\n", 149 | "\n", 150 | "Label: 1\n", 151 | "Index: 6945\tgah fuck tainting private account new follower xd\n", 152 | "\n", 153 | "Label: 1\n", 154 | "Index: 976\tbooooooo hot as mushroom stock wanna come get work\n", 155 | "\n", 156 | "Label: 1\n", 157 | "Index: 5431\tlet hate gambit grow embrace it spread it\n", 158 | "\n", 159 | "Label: 1\n", 160 | "Index: 6509\tand boycott fucking bar carry it\n", 161 | "\n" 162 | ] 163 | } 164 | ], 165 | "source": [ 166 | "# sample some of the online comments\n", 167 | "utils.sample_data(df, n=10) # feature engineering (create number hashtags col?)" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": 91, 173 | "metadata": {}, 174 | "outputs": [ 175 | { 176 | "name": "stdout", 177 | "output_type": "stream", 178 | "text": [ 179 | "Cyber Trolls\n", 180 | "('i', 2981)\n", 181 | "('hate', 1313)\n", 182 | "('damn', 1059)\n", 183 | "('fuck', 1036)\n", 184 | "('as', 1022)\n", 185 | "\n", 186 | "Non Cyber Trolls\n", 187 | "('i', 3839)\n", 188 | "('hate', 1488)\n", 189 | "('damn', 1307)\n", 190 | "('im', 988)\n", 191 | "('like', 942)\n" 192 | ] 193 | } 194 | ], 195 | "source": [ 196 | "# most common words by label\n", 197 | "trolls = Counter(' '.join(list(df[df.label == 1].text)).split())\n", 198 | "non_trolls = Counter(' '.join(list(df[df.label == 0].text)).split())\n", 199 | "\n", 200 | "print('Cyber Trolls')\n", 201 | "print(*trolls.most_common()[:5], sep='\\n')\n", 202 | "print('\\nNon Cyber Trolls')\n", 203 | "print(*non_trolls.most_common()[:5], sep='\\n')" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": 3, 209 | "metadata": {}, 210 | "outputs": [], 211 | "source": [ 212 | "## Save vectorizer in ./model_assets\n", 213 | "utils.persist_vectorizer(vectorizer, 'test_v.0.0') " 214 | ] 215 | }, 216 | { 217 | "cell_type": "markdown", 218 | "metadata": {}, 219 | "source": [ 220 | "## text feature extractors" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": 92, 226 | "metadata": {}, 227 | "outputs": [ 228 | { 229 | "name": "stdout", 230 | "output_type": "stream", 231 | "text": [ 232 | "True\n" 233 | ] 234 | } 235 | ], 236 | "source": [ 237 | "# bag-of-words encoding\n", 238 | "enc = utils.build_encoder(df.text, count_vectorizer=True)\n", 239 | "count_vectorized = enc.fit_transform(df.text).toarray()\n", 240 | "\n", 241 | "# tf-idf encoding\n", 242 | "enc = utils.build_encoder(df.text, tf_idf=True)\n", 243 | "tf_idf = enc.fit_transform(df.text).toarray()\n", 244 | "\n", 245 | "print(tf_idf.shape == count_vectorized.shape)" 246 | ] 247 | }, 248 | { 249 | "cell_type": "markdown", 250 | "metadata": {}, 251 | "source": [ 252 | "# Model dev" 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": 94, 258 | "metadata": {}, 259 | "outputs": [], 260 | "source": [ 261 | "X_train, X_test, y_train, y_test = train_test_split(count_vectorized, df.label)" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 95, 267 | "metadata": {}, 268 | "outputs": [ 269 | { 270 | "data": { 271 | "image/png": "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iIiIiIiIiIjIyUpz1mWDma0FigPrYlwVERERERHJGdWA3c656rGuiOQ/iiFFRERERPKdauRADKnGuuwpXqRIkdJ16tQpHeuKiIiIiIhI9i1fvpwDBzQ9jRw3iiFFRERERPKRnIoh1ViXPevq1KlTeunSpbGuh4iIiIiI5IBGjRrx/fffr4t1PSTfUgwpIiIiIpKP5FQMqTnrRERERERERERERERERGJEjXUiIiIiIiIiIiIiIiIiMaLGOhEREREREREREREREZEYUWOdiIiIiIiIiIiIiIiISIyosU5EREREREREREREREQkRtRYJyIiIiIiIiIiIiIiIhIjaqwTERERERERERERERERiZGCsa6AiIiISHJyMtu3b2fPnj0cOnQI51ysqyQi+YCZkZCQQLFixShdujRxceqrKCIiIpIfKIYUkeMhljGkGutEREQkppKTk/n999/Zv39/rKsiIvmMc46DBw9y8OBB9u3bx2mnnaYGOxEREZE8TjGkiBwvsYwh1VgnIiIiMbV9+3b2799PwYIFqVChAomJifoyXURyRHJyMvv27WPTpk3s37+f7du3U7Zs2VhXS0RERESyQTGkiBwvsYwhdRcTERGRmNqzZw8AFSpUoFixYgqyRCTHxMXFUaxYMSpUqACk3m9EREREJO9SDCkix0ssY0jdyURERCSmDh06BEBiYmKMayIi+VXg/hK434iIiIhI3qUYUkSOt1jEkGqsExERkZgKTASu3pAicryYGZB6vxERERGRvEsxpIgcb7GIIXVHExERERGRfC0QaImIiIiIiIhkJBYxpBrrRERERERERERERERERGJEjXUiIiIiIiIiIiIiIiIiMaLGOhERERHJE6pVq0a1atViXQ3JpHnz5mFmjBgxItZVERERERGRk5BiyLzlZI8hC8a6AiIiIiIZscdz93xT7rGcmXA4MCZ61apVWblyJYULFw7LU61aNX777TeSkpIoWDB3fZTbt28fY8eOZebMmSxbtoydO3dStGhRatWqxd///nd69+7NGWecEetqnjCPP/44Q4cOxcxYvXo1NWrUiHWVREREREROCoohUymGzDsUQ57c9Ms6ERERkVxm/fr1vPDCC7GuRpZ88803nHXWWdxzzz2sXr2ayy+/nEGDBtGzZ08KFy7M008/Te3atfn+++9jXdUTIjk5mfHjx2NmOOcYO3ZsrKt0wjVr1ozly5dz++23x7oqIiIiIiL5mmLIvE8xpGLI3NWULiIiInKSK1WqFGbGk08+SZ8+fShbtmysq5ShFStWcNlll7F3716eeuopBg4cGNZjc+3atQwePJjdu3fHqJYn1ieffML69evp3bs3M2bMYOLEiQwfPpz4+PhYV+2EKVq0KLVr1451NURERERE8jXFkPmDYkjFkPplnYiIiEguUrRoUR7KmX6zAAAgAElEQVR55BF2797N448/nqWyU6ZM4aKLLqJEiRIUKVKEevXq8eSTT3Lo0KGwvIGx+/fv3899991H1apVSUhIoGbNmjz99NM4l/lhWe644w52797N4MGDGTx4cMShVapXr86UKVNo2rQpAE2aNKFAgQKsW7cu4jafe+45zIxRo0aFrdu1axcDBgygcuXKFC5cmLPPPpuXXnopap2//fZbOnfuTIUKFShUqBCnnXYat956K3/++WdY3pYtW2JmHD58mGHDhnHWWWeRkJBAr169Mn0+gJRekH379qV79+5s3ryZmTNnRs3/559/0rNnT8qVK0eRIkVo0KABb7/9dtQx+1u0aEHBggU5dOgQQ4cOpVatWiQkJNCnT580+d555x1atmxJyZIlU87VE088weHDh8PqsHDhQjp06ECVKlVISEigQoUKNG3alOHDh6fJt2nTJu69917OOussEhMTKVmyJLVr1+amm25K83pGqvuZZ55J4cKF2bFjR8TzMGLECMyM119/PU3677//Tr9+/TjjjDNISEigTJkydOzYkaVLl0Y9pyIiIiIiJwPFkB7FkIohg+XFGFKNdSIiIiK5TP/+/alRowavv/46q1atylSZIUOG0K1bN5YvX0737t0ZMGAAzjmGDBnCZZddRlJSUliZpKQkLr30Uj744APatWtHnz59OHDgAA888ADDhg3L1H7Xrl3LvHnzKFy4MPfff3+G+RMSEgDo168fycnJUYf2GDduHAkJCfTs2TNN+uHDh2nTpg1z5szh2muv5ZZbbmHnzp3cddddDBgwIGw7EyZMoHnz5nzyySe0atWKu+++m8aNGzNu3DgaN27M+vXrI+7/mmuu4dVXX6VZs2bcfffd1KtXL8NjC9i4cSMfffQRderU4fzzz+emm24CYMyYMRHzb9q0iaZNmzJp0iTq1q3L3XffTf369enbty+vvPJKuvvq1KkT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8GvQqpV4jXW1gK9DylTEGwJzg3Nuv7/ffWb2B1DZzCo65zaGVO1M/3FVDhymiIiIiBwDxZCpFEPmP3GxroCIiIiIiIhIkMDkG0f9x8/9x7YR8l4EFAUWO+cOBaWnV6ZdSJ7slBEREREREck2NdaJiIiIiIjICWNmtcwsbLhJM4szs5HAqXiNbzv8Ve8DW4FrzaxxUP7CwAj/6T9DNjcBOAQMMLNqQWVKAUP8p6+FlAk8f8jPFyhTDejvb29Cpg5SREREREQkCzQMpoiIiIiIiJxIlwNPmtmXwFq8OeXKAxcDZwCbgFsCmZ1zu83sFrxGuwVm9i6wHbgSOMtPnxy8A+fcWjO7D3gJWGJmk4HDQGegCjDKOfd1SJnFZvY8cC/wk5m9DxQCugGlgTucc+ty8kSIiIiIiIiAGutERERERETkxJqHNwdcc6ABUBLYhzcf3FvAS8657cEFnHPTzexi4CHgGqAwsAavYe0lF2ECE+fcy2a2DhgE3Ig3sszPwMPOuTcjVcw5N9DMfgIGAH2BZOB74Fnn3KxsHreIiIiIiEhEaqwTERERERGRE8Y5twxvWMmslvsK71d5WSnzIfBhFsu8CURszBMRERERETkeNGediIiIiIiIiIiIiIiISIyosU5EREREREREREREREQkRtRYJyIiIiIiIiIiIiIiIhIjaqwTERERERERERERERERiRE11omIiIicJBYsWICZMXTo0FhXRTKpR48emBkbNmyIdVVEREREROQkoxgy71EMmXepsU5ERERyP7PcveSwFStWcMcdd1C3bl1KlChBoUKFqFSpEu3bt+eNN97g4MGDOb7P3Mg5R40aNTAzLrroolhXR0RERERE8opYx4iKIWNCMaTkZWqsExEREclFhg0bxjnnnMPo0aMpVqwYPXv2ZNCgQbRr144VK1bQp08fWrRoEetqnhDz5s3j119/xcxYtGgRK1asiHWVTrhnn32W5cuXU6FChVhXRUREREREciHFkKkUQyqGzMsKxroCIiIiIuJ54okneOyxxzjttNN47733uOCCC8LyzJo1i1GjRsWgdifemDFjALj//vt5+umnGTNmDM8//3yMa3ViVaxYkYoVK8a6GiIiIiIikgsphkxLMaRiyLxMv6wTERERyQXWrVvH0KFDiY+P5+OPP44YZAF06NCB2bNns2LFCsyMSy65JOo269WrR3x8PJs2bQpb9/XXX9OmTRtKlChBsWLFuOyyy1iyZEnE7Rw5coRXX32VJk2aULx4cYoWLUqDBg0YPXo0ycnJYcdhZvTq1YtVq1bRrVs3Tj31VOLi4liwYEGmz8eWLVuYMWMGderUYfjw4ZQtW5ZJkyZx6NChqGU++eQTmjVrRtGiRSldujSdOnVi1apVEcfsX7NmDWZGnz59WLlyJV26dKFcuXLExcXx5ZdfpuTbtm0bgwcPpnbt2hQpUoSSJUvy97//nXnz5oXt/9ChQ7zwwgs0aNCAUqVKkZiYSLVq1bjqqqv4/PPP0+RduHAhHTp0oEqVKiQkJFChQgWaNm3K8OHD0+QLrfuiRYswM7p27Rr1PJx55pkUKVKEnTt3hp2fdu3aUaZMGRISEqhRowb3338/u3fvjrotERERERHJnRRDpqUY0qMYMu9SY52IiIhILjBhwgSSkpK45pprqFu3brp5ExISqF27Nq1atWL+/PmsWrUqLM/ixYtZtmwZHTt2DBv+4ttvv6Vly5YkJCTQv39/2rVrx2effcaFF17IokWL0uRNSkqiQ4cO9O/fn507d9K9e3f69u1LcnIyd9xxBz179oxYx19++YULLriAdevWcf3119O3b1+KFy+e5fPRq1cv4uPj6d69O9u2bWPq1KkR87/zzju0b9+eH3/8kW7dunHrrbeybds2mjZtyvr166PuZ9WqVZx//vls2LCBHj16cMstt1CsWDEA1q5dS6NGjXjmmWcoX748t99+O126dGHZsmVceumlTJgwIc22brjhBu655x6Sk5Pp2bMnd9xxBxdeeCH/+c9/mDt3bkq+WbNm0apVKxYvXkybNm0YOHAgHTt2JD4+ntdeey3d83LhhRdSo0YNZs6cyY4dO8LWL168mDVr1tCxY0dKliyZkv7oo49y+eWX891333HFFVdw5513UqNGDZ599lmaN2/Onj170t2viIiIiIjkLoohI58PxZBpKYbMOzQMpoiIiEguEOiJ17p160yX6devH/Pnz2fMmDE899xzadYFhv+49dZbw8rNnj2bl19+mQEDBqSkzZgxg6uuuoqbb76ZlStXEhfn9ekaOXIkc+bMYcCAAbzwwgsUKFAAgKNHj9K3b1/Gjx9P586d6dixY9jxPPjggzzxxBOZPp4A5xzjxo2jQIEC3HDDDQDcdNNNvPTSS4wZM4brrrsuTf5du3bRr18/ChUqxDfffEO9evVS1g0aNCjdIV8WLVrEI488wrBhw8LW3XDDDaxfv54pU6bQpUuXlPQdO3Zw0UUXMWDAADp06EC5cuXYvn0777//PhdccAGLFy9OOX8B27ZtS/l77NixOOf44osvwoLqrVu3Znh+evbsyaOPPsrkyZO57bbb0qx78803U/IEfPrppwwfPpwWLVowa9YsSpQokbJu3Lhx3HLLLQwbNoxnn302w32LiIiIiEjuoBgylWLI9CmGzBv0yzoRERGRXGDjxo0AVKlSJdNlrrrqKipVqsTEiRPTDO2xc+dOpkyZQo0aNWjTpk1YuZo1a9KvX780aR07duTiiy9mzZo1KT0jk5OTGT16NBUqVOAf//hHSpAFUKBAAUaNGoWZ8c4774Tto3z58jz22GOZPpZg8+fPZ/Xq1Vx22WUpY+2fe+651K9fnwULFrB69eo0+adNm8bu3bu58cYb0wRZ4PUGTK83ZqVKlXj44YfD0pcuXcpXX31Ft27d0gRZAKVKlWLo0KHs37+fadOmAWBmOOdISEgIC7IAypQpE5ZWtGjRsLSyZctGrWvAjTfeiJmlBFUBBw8eZMqUKVSsWJFLL700Jf2ll14CvKAqOMgC6NOnD3Xr1o34GoqIiIiISO6lGDKVYsj0KYbMG/TLOhEREZFcwDkHeB/YM6tgwYL06dOHYcOG8cEHH9C9e3cA3nrrLQ4cOEDfvn0jbu/CCy+MGAy0bNmShQsX8sMPP3DxxRezatUqtm3bxplnnsmIESMi1qFIkSIsX748LL1+/fokJCSEpU+dOpWffvopTVrDhg258sorU56PHTsW8HpCBuvZsyf33nsvY8eO5ZlnnklJ/+GHHwBo0aJF2P6KFy/O3/72tzRzCAQ799xzKVSoUFj6119/DXg9IIcOHRq2fvPmzQApx16qVCnatWvHJ598QoMGDbj66qu58MILueCCCyhSpEiastdffz0zZ86kcePGdOvWjVatWtG8eXMqV64csY6hTj/9dFq2bJkyfE2tWrUAr2frzp07ueWWW9IExV9//TUJCQn861//iri9I0eOsHHjRnbt2hUWiImIiIiISO6kGFIxpGLI/EWNdSIiIiK5QKVKlVixYkWaCawzo2/fvjzxxBO8/vrrKYHWmDFjKFSoUFigElC+fPmI6YF5CXbt2gWkDruxevVqHn/88ah12Lt3b9RthZo6dWpYD7zevXunBFrbtm1j2rRplC5dOk3wBd5E2YMHD2bixImMGDEiJUAK1DfacUVLT6+egWOfM2cOc+bMiVo++Njff/99nnrqKf71r3/x6KOPAl4g2qVLF5577jnKlSsHQNeuXSlSpAjPP/8848aNS5lj4LzzzuPJJ5/M1DA2vXr1Yv78+bz55puMHDkSiDx8CcD27dtxzqX7GgaORYGWiIiIiEjeoBhSMaRiyPxFw2CKiIiI5AKBHn2fffZZlspVrlyZK664gi+++ILly5enTAreqVOnlA/2oQI9+kJt2rQJIOXDduCxU6dOOOeiLmvXrg3bVrTenW+//XZY+Xs/wnsAACAASURBVHHjxqWsDwzHsn37dhISEjCzlOXUU08lKSmJLVu2MH369JQygSFKoh1XtPT06hk49ldeeSXdYw/04ARvSJJhw4axevVqfvvtN9566y2aNm3KpEmT6Nq1a5rtX3HFFcyfP5+dO3cyb9487rrrLn766Sc6dOjAypUro9Y34JprruGUU07hrbfeIjk5mU2bNjF37lwaNWrEOeeckyZv8eLFKVeuXLrH4ZzLdK9MERERERGJPcWQHsWQiiHzCzXWiYiIiOQCN910E/Hx8XzwwQf8/PPP6eYNnlsASJk7YMyYMelOCh7w5ZdfkpycHJa+YMECABo0aABA7dq1KVmyJN988w1JSUmZPpbsCARd3bt3p3fv3mHL1VdfDZAmwAnUN9IwJbt37w4bMiUzmjRpApAy90JWVa1alR49ejB37lyqV6/OggULUnpvBktMTKR169a88MILDB48mIMHDzJ79uwMt5+YmEjnzp35/fffmT9/Pm+//TZHjx4N6xEZOJYtW7ZkKoATEREREZG8QTGkRzGkYsj8Qo11IiIiIrlAtWrVGDp0KIcPH6Z9+/YsWbIkYr7Zs2fTrl27NGmtW7emVq1avPnmm0yZMoVatWrRqlWrqPtavXo1r776apq0GTNmsHDhQmrWrMmFF14IePMZ3HHHHWzcuJE777yTAwcOhG1r48aNGQaGmfXFF1+wYsUK6tWrxzvvvMO4cePClilTplClShU+++wzfv31V8DrtVmsWDEmTZrEsmXL0mxz2LBh7N69O8t1adKkCU2bNmXKlClhk3AH/Pjjj2zduhWAv/76K2zfAPv27WPfvn3Ex8dTsKA3Av3ChQs5evRoWN5A781Ik4ZH0qtXLwAmTZrEpEmTiI+PTxnGJti9994LeBOBByahD7Z3716+/fbbTO1TRERERERyB8WQiiEVQ+YvmrNOREREJJcYMmQIR44c4fHHH+e8886jWbNmNG7cmFNOOYXNmzfzxRdfsHr1aho3bpymnJlx2223pXygTq9HJEDbtm0ZOHAgn3zyCfXr12fNmjVMnTqVwoUL88Ybb6SZOPyRRx7hxx9/5LXXXuPDDz/kkksuoXLlyvz111+sXr2ar776ipEjR3L22Wdn+/gDPTr79OkTNU+BAgXo1ev/2bv7qL3K+l7w3x8gcUABI84RxTZAEZ1qxgMiR1pR4NQRD0dFcUFXKeBrdREUiExneCuD2HokgEqstOgAlbbhCAePiUA7LcYAyRIJakqxKmKqMOBbmPASiUKu+ePeD33Ozf2E58nzkJ3A57PWva7sva/fde0d1wpr+93X3sfn3HPPzSWXXJI/+7M/yy677JKFCxfm+OOPzwEHHJCjjjoqL3zhC3PTTTfln//5n/O6170uN95448gPom/MokWLcuihh+b444/PJz/5ybzmNa/JLrvskrvvvjvf+ta3cscdd+Qb3/hGdt111/zoRz/K/vvvn7lz52bu3LnZfffds3bt2ixZsiQ//elPc8opp2THHXdMkpxwwgn5yU9+kt/93d/NnDlzst122+XWW2/N0qVLs8ceezzhdScTOeigg7LHHnvkb//2b/PrX/86RxxxRJ7//Oc/od8b3/jGnHvuuTnzzDOz995757DDDssee+yRhx56KKtXr87Xvva1HHzwwVmyZMmU/n4AAIB+uYd0D+ke8unDyjoAgC3IWWedldtvvz3z5s3L2rVrc+mll+a8887LV77yley111753Oc+N/JVHccff3y22WabzJo1a+RrLMY74IADsnTp0qxfvz4LFy7Mddddl0MOOSTLli3LQQcd9D/0fdaznpUvfelL+au/+qvss88+WbJkSc4///xcf/312bBhQz760Y/mD/7gD6Z93ffff3+uvvrqzJo1K8ccc8xG+77nPe9JVeWyyy57/NUqxx57bL785S9n7ty5WbRoUS6++OLMnj07K1asePwpw7HvEkzWb/zGb2TlypX56Ec/mqrKX//1X+eiiy7KihUrsscee+Qv//IvH7/B3GuvvXL2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\n", 272 | "text/plain": [ 273 | "" 274 | ] 275 | }, 276 | "metadata": { 277 | "image/png": { 278 | "height": 316, 279 | "width": 885 280 | }, 281 | "needs_background": "light" 282 | }, 283 | "output_type": "display_data" 284 | } 285 | ], 286 | "source": [ 287 | "# Show of labels train/test sets\n", 288 | "fig, axs = plt.subplots(1, 2, figsize=(15,5))\n", 289 | "\n", 290 | "train_count = Counter(y_train)\n", 291 | "axs[0].set_title('Distribution of CyberTrolls - Train set')\n", 292 | "axs[0].bar(list(train_count.keys())[0], list(train_count.values())[0], align='center', color='g', label='Non Cyber-Agressive')\n", 293 | "axs[0].bar(list(train_count.keys())[1], list(train_count.values())[1], align='center', color='r', label='Cyber-Agressive')\n", 294 | "axs[0].set_xticks(list(set(y_train)))\n", 295 | "axs[0].legend()\n", 296 | "\n", 297 | "test_count = Counter(y_test)\n", 298 | "axs[1].set_title('Distribution of CyberTrolls - Test set')\n", 299 | "axs[1].bar(list(test_count.keys())[0], list(test_count.values())[0], align='center', color='g', label='Non Cyber-Agressive')\n", 300 | "axs[1].bar(list(test_count.keys())[1], list(test_count.values())[1], align='center', color='r', label='Cyber-Agressive')\n", 301 | "axs[1].set_xticks(list(set(y_test)))\n", 302 | "axs[1].legend()\n", 303 | "\n", 304 | "plt.show()" 305 | ] 306 | }, 307 | { 308 | "cell_type": "code", 309 | "execution_count": 186, 310 | "metadata": {}, 311 | "outputs": [ 312 | { 313 | "name": "stdout", 314 | "output_type": "stream", 315 | "text": [ 316 | " Train Accuracy: 0.97\n", 317 | " Test Accuracy: 0.85\n", 318 | "[[2456 593]\n", 319 | " [ 166 1786]]\n" 320 | ] 321 | } 322 | ], 323 | "source": [ 324 | "from BaseModel import SVM\n", 325 | "\n", 326 | "# svc params\n", 327 | "params = {'C': np.logspace(-5, 5, 5)}\n", 328 | "data = {'X_train': X_train, 'X_test': X_test, 'y_train': y_train, 'y_test': y_test}\n", 329 | "clf = SVM(description='dev')\n", 330 | "clf.train(data=data, **params)\n", 331 | "clf.display_results(data)" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": 193, 337 | "metadata": {}, 338 | "outputs": [ 339 | { 340 | "ename": "AttributeError", 341 | "evalue": "'SVM' object has no attribute 'display_results_'", 342 | "output_type": "error", 343 | "traceback": [ 344 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 345 | "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", 346 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mclf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdisplay_results_\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 347 | "\u001b[0;31mAttributeError\u001b[0m: 'SVM' object has no attribute 'display_results_'" 348 | ] 349 | } 350 | ], 351 | "source": [ 352 | "clf.display_results_" 353 | ] 354 | }, 355 | { 356 | "cell_type": "markdown", 357 | "metadata": {}, 358 | "source": [ 359 | "## save model and vectorizer" 360 | ] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "execution_count": 10, 365 | "metadata": {}, 366 | "outputs": [ 367 | { 368 | "name": "stdout", 369 | "output_type": "stream", 370 | "text": [ 371 | "Model Saved.\n" 372 | ] 373 | } 374 | ], 375 | "source": [ 376 | "persist_model(clf, 'test_v.0.0')\n", 377 | "persist_model(clf, 'test_v.0.0')" 378 | ] 379 | }, 380 | { 381 | "cell_type": "code", 382 | "execution_count": 165, 383 | "metadata": {}, 384 | "outputs": [ 385 | { 386 | "data": { 387 | "text/plain": [ 388 | "(15000, 16673)" 389 | ] 390 | }, 391 | "execution_count": 165, 392 | "metadata": {}, 393 | "output_type": "execute_result" 394 | } 395 | ], 396 | "source": [ 397 | "data['X_train'].shape" 398 | ] 399 | }, 400 | { 401 | "cell_type": "code", 402 | "execution_count": null, 403 | "metadata": {}, 404 | "outputs": [], 405 | "source": [] 406 | } 407 | ], 408 | "metadata": { 409 | "kernelspec": { 410 | "display_name": "Python 3", 411 | "language": "python", 412 | "name": "python3" 413 | }, 414 | "language_info": { 415 | "codemirror_mode": { 416 | "name": "ipython", 417 | "version": 3 418 | }, 419 | "file_extension": ".py", 420 | "mimetype": "text/x-python", 421 | "name": "python", 422 | "nbconvert_exporter": "python", 423 | "pygments_lexer": "ipython3", 424 | "version": "3.5.6" 425 | } 426 | }, 427 | "nbformat": 4, 428 | "nbformat_minor": 2 429 | } 430 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.8.0 2 | bs4==0.0.1 3 | certifi==2019.6.16 4 | chardet==3.0.4 5 | Click==7.0 6 | Flask==1.1.1 7 | idna==2.8 8 | itsdangerous==1.1.0 9 | Jinja2==2.10.1 10 | joblib==0.13.2 11 | MarkupSafe==1.1.1 12 | nltk==3.4.5 13 | numpy==1.17.0 14 | pandas==0.25.1 15 | python-dateutil==2.8.0 16 | pytz==2019.2 17 | scikit-learn==0.21.3 18 | scipy==1.3.1 19 | six==1.12.0 20 | sklearn==0.0 21 | soupsieve==1.9.3 22 | urllib3==1.25.3 23 | Werkzeug==0.15.5 24 | -------------------------------------------------------------------------------- /static/screen-shot-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgopar/ml-flask-web-app/df90234a2aa2e3b0009292fdc356dfb6a5c05bcb/static/screen-shot-ui.png -------------------------------------------------------------------------------- /static/troll-guy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgopar/ml-flask-web-app/df90234a2aa2e3b0009292fdc356dfb6a5c05bcb/static/troll-guy.png -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |

Welcome to the Cyber-Troll Prediction Service

8 |
9 | 10 |
11 | 12 |
13 | 14 |
15 | 16 | This is an example of a machine learning model being deployed using Flask and 17 | Scikit-Learn! You can interact with the machine learning model that was built 18 | to predict whether or not an online comment is from a cyber troll. Enter a comment 19 | below for a prediction! 20 | 21 |
22 | 23 |
24 |
25 | 26 | 27 |
28 |
29 | 30 |

{{ submission }}

31 |

{{ text }}

32 | 33 | 34 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | from bs4 import BeautifulSoup 2 | from nltk.corpus import stopwords 3 | from nltk.stem import WordNetLemmatizer 4 | from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer 5 | import pandas as pd 6 | import pickle 7 | import string 8 | import json 9 | import random 10 | import os 11 | 12 | 13 | def load_data(raw=None): 14 | """ load data to development workspaces 15 | 16 | Parameters 17 | -------------- 18 | raw: (bool) if True, function returns cleaned dataset. 19 | return (df) data frame of data and its labels 20 | """ 21 | raw_data = [] 22 | with open('./data/cyber_data.json') as f: 23 | for line in f: 24 | raw_data.append(json.loads(line)) 25 | 26 | labels = [int(d['annotation']['label'][0]) for d in raw_data] 27 | text = [d['content'] for d in raw_data] 28 | data = {'text': text, 'label': labels} 29 | df = pd.DataFrame(data, columns=['text', 'label']) # raw data frame 30 | 31 | if raw: 32 | return df 33 | else: 34 | df.text = df.text.apply(clean_text) 35 | return df 36 | 37 | 38 | def clean_text(text): 39 | """ clean input text for the prediction model 40 | 41 | Parameters 42 | ------------- 43 | text: (str) text to clean 44 | return (str) post-processed clean text 45 | """ 46 | lemmatizer = WordNetLemmatizer() 47 | punctuation = list(string.punctuation) 48 | punctuation.extend(['.', "’", ',']) 49 | text = BeautifulSoup(text, 'html.parser').text 50 | filtered_text = ' '.join([word.lower() for word in text.split() if word not in stopwords.words('english')]) 51 | filtered_text = ''.join([c for c in filtered_text if c not in punctuation]) 52 | filtered_text = ''.join([c for c in filtered_text if not c.isdigit()]) 53 | filtered_text = filtered_text.replace('-', ' ') 54 | filtered_text = ' '.join([lemmatizer.lemmatize(w) for w in filtered_text.split()]) 55 | return filtered_text 56 | 57 | 58 | def persist_model(clf, description): 59 | """ saves pickled classifier in /model_assets folder with naming convention: model_[description].pkl 60 | 61 | Parameters 62 | ------------- 63 | clf: (obj) scikit-learn trained model 64 | description: (str) model version/descriptor 65 | """ 66 | model_path = open(os.path.join(os.pardir, "model_assets/model_{}.pkl".format(description)), "wb") 67 | pickle.dump(clf, model_path) 68 | print('Model Saved.') 69 | 70 | 71 | def build_encoder(text, count_vectorizer=None, tf_idf=None): 72 | """ builds a text feature extractor given an iterable of text data 73 | 74 | Parameters 75 | --------------- 76 | text: (list or series) of text data to transoform 77 | count_vectorizer: (bool) If `True` transforms into BoW model 78 | tf_idf: (bool) If `True` transforms into TF-IDF representation 79 | 80 | """ 81 | 82 | if count_vectorizer: 83 | vectorizer = CountVectorizer() 84 | vectorizer.fit(text) 85 | return vectorizer 86 | 87 | if tf_idf: 88 | transformer = TfidfVectorizer() 89 | transformer.fit(text) 90 | return transformer 91 | 92 | 93 | def persist_vectorizer(vectorizer, description): 94 | """ saves bag-of-words vectorizer in /model_assets folder with naming convention: vectorizer_[description].pkl 95 | 96 | Parameters 97 | ------------- 98 | vectorizer: (obj) sklearn vectorizer object 99 | description: (str) vectorizer version/descriptor 100 | """ 101 | vectorizer_path = open(os.path.join(os.pardir, "model_assets/vectorizer_{}.pkl".format(description)), "wb") 102 | pickle.dump(vectorizer, vectorizer_path) 103 | print('Vectorizer Saved.') 104 | 105 | 106 | def sample_data(df, n): 107 | """ prints to console n random samples of data in the data frame (e.g online comment and label) 108 | 109 | Parameters 110 | ------------- 111 | df: pandas DataFrame to be sampled 112 | n: number of samples to generate 113 | 114 | """ 115 | for label in set(df.label): 116 | subset = df[df.label == label] 117 | rand_idxs = [random.randint(0, subset.shape[0]) for _ in range(n)] 118 | for idx in rand_idxs: 119 | print('Label: {}\nIndex: {}\t{}\n'.format(subset.iloc[idx]['label'], idx, subset.iloc[idx]['text'])) --------------------------------------------------------------------------------