2024-06-02 17:59:28 +02:00
{
"cells": [
{
"cell_type": "markdown",
"source": [
"### Fasttext --> Feedforward Neural Network"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 3,
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"\n",
"train = pd.read_csv(\"train.csv\")\n",
"test = pd.read_csv(\"test.csv\")\n",
"valid = pd.read_csv(\"valid.csv\")\n",
"\n",
"train.loc[train[\"review_score\"]==-1, \"review_score\"]=0\n",
"test.loc[test[\"review_score\"]==-1, \"review_score\"]=0\n",
"valid.loc[valid[\"review_score\"]==-1, \"review_score\"]=0"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"Wczytywanie pliku z wyuczonymi angielskimi embeddingami fasttext (https://fasttext.cc/docs/en/crawl-vectors.html):"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 4,
"outputs": [
{
"data": {
"text/plain": "array([ 1.78246237e-02, -1.06189400e-01, -2.44729444e-02, -2.21012142e-02,\n 1.59936324e-02, 3.34691778e-02, -1.06833659e-01, -3.30949295e-03,\n -2.14542568e-04, 1.48508116e-03, -2.93094777e-02, -2.15188637e-02,\n 3.55641954e-02, 2.25957129e-02, 2.12054476e-02, 4.47175875e-02,\n -5.20626409e-03, 5.60886189e-02, 1.39281712e-02, 3.23922932e-02,\n 2.46591270e-02, 9.72066727e-03, -8.12041387e-03, -4.68677804e-02,\n 3.57795991e-02, -3.31927240e-02, 1.93208049e-04, -2.41375212e-02,\n 1.56584624e-02, 9.49325413e-02, 3.46111394e-02, -1.00905932e-02,\n 4.39240132e-03, 2.39842106e-02, 1.37603339e-02, -4.40947488e-02,\n 7.08558830e-03, 8.42494331e-03, 7.17797317e-03, 7.57907005e-03,\n 2.67372504e-02, -3.01967952e-02, -4.28102091e-02, 2.58105062e-02,\n 5.47624193e-02, 5.96127613e-03, -1.95893943e-02, 4.33259085e-02,\n -2.37351451e-02, 8.60271696e-03, 5.02952486e-02, -1.85346920e-02,\n 4.15457152e-02, 1.24201048e-02, -8.80148821e-03, 3.67457941e-02,\n 6.00036196e-02, -1.33206621e-02, -9.77441017e-03, 8.50583613e-03,\n 7.39839450e-02, 1.42067904e-02, -5.09730168e-03, -2.48528309e-02,\n -3.16993035e-02, -1.11876670e-02, -3.96261504e-03, 7.88500253e-03,\n 2.44743899e-02, -3.55262943e-02, 1.29078805e-01, -1.49423713e-02,\n -6.50351308e-03, -2.00819727e-02, -2.81756446e-02, -2.70206425e-02,\n -1.32226339e-02, -2.91594937e-02, 8.19074363e-03, 1.02669047e-03,\n 7.59374257e-03, -3.04275062e-02, 1.17772142e-03, -8.05200636e-03,\n 2.26024836e-02, -1.43838655e-02, -6.42339066e-02, -1.50536001e-03,\n -6.04793020e-02, -6.25816360e-02, -3.21857110e-02, 2.24062111e-02,\n 1.76566597e-02, 2.77742837e-03, -7.06164539e-03, -2.49959864e-02,\n -2.42400132e-02, -3.27228429e-03, 1.67806149e-02, 1.08696334e-03,\n -8.86186119e-03, 1.75914317e-02, 2.39897612e-03, -1.10858642e-02,\n 4.37321281e-03, -1.52284997e-02, 1.28000462e-02, 3.08077410e-02,\n -3.72859016e-02, 1.01946890e-02, 2.72647869e-02, 4.09879610e-02,\n -5.15891647e-04, -1.48480488e-02, 3.46730947e-02, 3.72706391e-02,\n -3.27323265e-02, -2.06248090e-02, 5.93318080e-04, -1.00421635e-02,\n 5.68121858e-03, 3.50009021e-03, 5.35131479e-03, -4.51390296e-02,\n -2.47980915e-02, 1.45408430e-03, 2.12197360e-02, 1.54698789e-02,\n -8.19472875e-03, -1.68298408e-02, 6.03655400e-03, -2.55106628e-04,\n 3.55045721e-02, -3.14546265e-02, -1.14588011e-02, 4.87726405e-02,\n -4.81645577e-02, 2.94467416e-02, -1.99259575e-02, 3.88172567e-02,\n 2.61239633e-02, 2.75779348e-02, -1.46336835e-02, 1.05489865e-02,\n 2.05413140e-02, -6.39167754e-03, -1.77114069e-01, 4.08548955e-03,\n 1.85309928e-02, 1.64314881e-02, -4.22716849e-02, -4.01179083e-02,\n -5.73144108e-02, 2.58368440e-02, -5.67379827e-03, 1.25513151e-02,\n -9.11396556e-03, 3.03542819e-02, 5.28743342e-02, 2.66610682e-02,\n 3.72167975e-02, 1.17919482e-02, 4.31168377e-02, -4.78969961e-02,\n 4.30925563e-02, -1.19056599e-02, 2.69640237e-02, 7.23459641e-04,\n -4.54982556e-02, 1.49192475e-02, -8.30576941e-03, -3.83936167e-02,\n -1.41146081e-03, -4.87017911e-03, -2.61655133e-02, -1.40941609e-02,\n -8.20814539e-03, -4.31758128e-02, 6.44639693e-03, -2.87407413e-02,\n 8.11430439e-03, 3.59113403e-02, -9.36233252e-03, 1.77651364e-02,\n 4.21329997e-02, 1.47886062e-02, 2.88083218e-02, -6.43404340e-03,\n 1.00347018e-02, -6.53128885e-03, 2.48425044e-02, -2.85518263e-02,\n -2.17140149e-02, -1.74901895e-02, -2.42289691e-03, -4.53775264e-02,\n -1.26110762e-02, 1.16701461e-02, 4.70486209e-02, 1.68762766e-02,\n 1.47036910e-02, 8.21339190e-02, 7.89464638e-03, 8.35293531e-03,\n -5.81975766e-02, -8.22040439e-03, 1.47995166e-02, 1.86446775e-02,\n 7.70702632e-03, -2.84116622e-03, -3.67019586e-02, -8.45937431e-03,\n -3.01996600e-02, -2.45970348e-03, 3.84
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from gensim.models.fasttext import load_facebook_model\n",
"\n",
"vectors_file = \"cc.en.300.bin\"\n",
"fb_model = load_facebook_model(vectors_file)\n",
"\n",
"fb_model.wv.get_sentence_vector(\"Good game\")"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 5,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.9303907\n",
"0.94947904\n"
]
}
],
"source": [
"from numpy.linalg import norm\n",
"def cosine_similarity(a, b):\n",
" return np.dot(a,b)/(norm(a)*norm(b))\n",
"\n",
"def get_sentence_similarity(sent_a, sent_b):\n",
" vec1 = fb_model.wv.get_sentence_vector(sent_a)\n",
" vec2 = fb_model.wv.get_sentence_vector(sent_b)\n",
" return cosine_similarity(vec1,vec2)\n",
"\n",
"print(get_sentence_similarity(\"Good game\", \"Amazing game, I love it!\"))\n",
"print(get_sentence_similarity(\"Good game\", \"Horrible game. A buggy mess.\"))"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 6,
"outputs": [],
"source": [
"train[\"vectorized\"] = train[\"review_text\"].apply(lambda x : fb_model.wv.get_sentence_vector(x))"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 7,
"outputs": [
{
"data": {
"text/plain": "array([ 8.71886499e-03, -9.33922902e-02, -3.61323059e-02, -2.44729072e-02,\n 1.08409366e-02, 1.93068106e-02, -8.36923420e-02, 2.29316903e-03,\n -1.35135755e-03, 2.88301776e-03, -2.74441373e-02, -9.83558595e-03,\n 3.87936011e-02, 9.47409403e-03, 3.30047533e-02, 4.45564575e-02,\n 8.05891701e-04, 4.50393967e-02, 3.19698919e-03, 2.77824774e-02,\n -4.72737849e-03, 1.12508880e-02, 2.10752850e-03, -1.86015293e-02,\n 2.95459442e-02, -1.79125778e-02, -9.69705731e-03, -1.25571457e-03,\n 6.71224575e-03, 9.82025936e-02, 2.62354501e-02, 4.77575843e-04,\n 4.30168724e-03, 2.30760872e-02, 7.46196136e-03, -2.56011821e-02,\n 2.26500910e-03, 9.29989759e-03, 4.85312892e-03, -2.05451786e-03,\n 1.66831594e-02, -4.36836779e-02, -3.61202359e-02, 1.47827193e-02,\n 4.84717675e-02, 2.09881645e-02, -1.83120575e-02, 3.67828794e-02,\n -2.54551638e-02, 5.68488985e-03, 3.21363397e-02, -1.92501992e-02,\n 3.51247750e-02, 6.23576110e-03, -7.48702977e-03, 2.46541500e-02,\n 4.22804020e-02, -1.25615057e-02, -5.79859968e-03, 4.38177120e-03,\n 6.56097755e-02, 4.94229654e-03, 8.99066159e-04, -1.51860202e-02,\n -1.56211155e-02, -1.14172837e-02, 2.29500444e-03, 7.24412594e-03,\n 1.61466487e-02, -2.63501853e-02, 1.06835075e-01, -1.32511724e-02,\n 1.00777773e-02, -1.87501553e-02, -1.71737131e-02, -1.66921876e-02,\n -2.48353798e-02, -3.03455871e-02, -7.35406764e-03, 1.86671335e-02,\n 1.11391433e-02, 1.50522811e-03, 7.59978918e-03, -1.43874844e-03,\n 7.25007243e-03, -8.30824673e-03, -4.84226309e-02, 7.33444653e-03,\n -2.85197571e-02, -4.60745730e-02, -2.38343086e-02, 1.97487529e-02,\n 4.29472663e-02, -5.94962295e-03, 7.63149792e-03, -9.71615221e-03,\n -1.44147705e-02, 3.04668932e-03, 2.28872746e-02, 9.57457058e-04,\n -1.47111658e-02, 1.44942962e-02, -2.48183205e-04, -1.70479212e-02,\n 9.38933901e-03, -2.18814029e-03, 2.38826945e-02, 2.83374488e-02,\n -4.21173871e-02, 1.50122121e-02, 2.29728036e-02, 2.54183225e-02,\n -8.07054807e-04, -1.51364841e-02, 3.09927687e-02, 3.13952193e-02,\n -1.21332435e-02, -1.32499263e-02, -9.75007843e-03, -5.41008521e-05,\n -9.06463992e-03, -1.11404026e-03, -6.34767395e-03, -2.95756236e-02,\n -1.97567306e-02, -2.34055445e-02, 1.63229201e-02, 3.15271169e-02,\n -2.27316860e-02, -1.17390202e-02, -7.85546657e-03, -1.27675962e-02,\n 4.69210669e-02, -5.10264486e-02, -1.76178105e-03, 2.61727888e-02,\n -5.78924827e-02, 1.59178916e-02, -1.30226174e-02, 3.05363275e-02,\n 2.62711458e-02, 2.42318660e-02, 1.94618828e-03, 1.08218072e-02,\n 3.02217435e-02, 1.15953386e-02, -1.63421690e-01, 1.32743130e-02,\n 9.48617421e-03, -2.56551709e-03, -4.67495248e-02, -3.61125320e-02,\n -4.41548303e-02, 2.86387391e-02, -1.81056850e-04, -2.06265738e-03,\n 1.14647327e-02, 4.72528152e-02, 4.42985930e-02, 8.55786633e-03,\n 3.78191061e-02, 1.83753986e-02, 4.15914468e-02, -4.11635116e-02,\n 3.33526842e-02, -1.25481822e-02, 2.34596524e-02, 8.43344163e-03,\n -4.16795984e-02, 7.25348853e-03, -2.80407108e-02, -3.69634405e-02,\n -2.46312029e-05, -9.05072596e-03, -2.41407454e-02, -9.63037368e-03,\n 7.61253759e-03, -2.81870961e-02, -5.51456306e-03, -2.98725273e-02,\n 1.28271757e-03, 3.05276066e-02, -3.51019343e-03, 2.49049999e-02,\n 4.08466980e-02, 1.64877940e-02, 3.08923759e-02, 8.13027727e-04,\n 1.61136538e-02, -4.12218878e-03, 2.07552351e-02, -1.96164269e-02,\n -1.81689784e-02, -2.35741213e-02, 1.90784093e-02, -2.61228625e-02,\n 5.52171562e-03, 3.33901797e-03, 3.08879260e-02, 1.22919949e-02,\n 1.14197442e-02, 4.92207371e-02, -1.31729420e-03, 1.34627940e-02,\n -3.66763920e-02, -9.86301922e-04, -3.99103155e-03, 2.03184932e-02,\n -8.94649187e-04, -3.81594640e-03, -3.50187197e-02, -8.03904049e-03,\n -3.02518457e-02, 9.22963768e-03, 2.14
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train.iloc[0][\"vectorized\"]"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 8,
"outputs": [],
"source": [
"test[\"vectorized\"] = test[\"review_text\"].apply(lambda x : fb_model.wv.get_sentence_vector(x))\n",
"valid[\"vectorized\"] = valid[\"review_text\"].apply(lambda x : fb_model.wv.get_sentence_vector(x))"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 9,
"outputs": [],
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"from keras import layers\n",
"from keras.optimizers import Adam\n",
"\n",
"def create_model():\n",
" inputs = keras.Input(shape=(300,))\n",
" dense1 = layers.Dense(256, activation=\"relu\")(inputs)\n",
" dense2 = layers.Dense(128, activation=\"relu\")(dense1)\n",
" dense3 = layers.Dense(64, activation=\"relu\")(dense2)\n",
" output = layers.Dense(1, activation=\"sigmoid\")(dense3)\n",
" model = keras.Model(inputs=inputs, outputs=output)\n",
" model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=1e-4), metrics=['accuracy'])\n",
" return model"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 10,
"outputs": [],
"source": [
"train_x = np.stack(train[\"vectorized\"].values)\n",
"train_y = np.stack(train[\"review_score\"].values)\n",
"\n",
"valid_x = np.stack(valid[\"vectorized\"].values)\n",
"valid_y = np.stack(valid[\"review_score\"].values)\n",
"\n",
"test_x = np.stack(test[\"vectorized\"].values)\n",
"test_y = np.stack(test[\"review_score\"].values)"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 11,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 1/25\n",
"1351/1351 [==============================] - 3s 1ms/step - loss: 0.6815 - accuracy: 0.5627 - val_loss: 0.6221 - val_accuracy: 0.7079\n",
"Epoch 2/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6564 - accuracy: 0.6112 - val_loss: 0.6649 - val_accuracy: 0.5497\n",
"Epoch 3/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6492 - accuracy: 0.6204 - val_loss: 0.6619 - val_accuracy: 0.5553\n",
"Epoch 4/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6439 - accuracy: 0.6280 - val_loss: 0.6552 - val_accuracy: 0.5702\n",
"Epoch 5/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6405 - accuracy: 0.6333 - val_loss: 0.6135 - val_accuracy: 0.6346\n",
"Epoch 6/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6374 - accuracy: 0.6352 - val_loss: 0.7130 - val_accuracy: 0.4843\n",
"Epoch 7/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6348 - accuracy: 0.6409 - val_loss: 0.6140 - val_accuracy: 0.6315\n",
"Epoch 8/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6307 - accuracy: 0.6445 - val_loss: 0.5606 - val_accuracy: 0.7186\n",
"Epoch 9/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6297 - accuracy: 0.6449 - val_loss: 0.6899 - val_accuracy: 0.5398\n",
"Epoch 10/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6267 - accuracy: 0.6480 - val_loss: 0.6795 - val_accuracy: 0.5511\n",
"Epoch 11/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6251 - accuracy: 0.6486 - val_loss: 0.6778 - val_accuracy: 0.5430\n",
"Epoch 12/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6222 - accuracy: 0.6530 - val_loss: 0.5447 - val_accuracy: 0.7297\n",
"Epoch 13/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6206 - accuracy: 0.6541 - val_loss: 0.5630 - val_accuracy: 0.7080\n",
"Epoch 14/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6189 - accuracy: 0.6563 - val_loss: 0.5919 - val_accuracy: 0.6746\n",
"Epoch 15/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6168 - accuracy: 0.6588 - val_loss: 0.6887 - val_accuracy: 0.5485\n",
"Epoch 16/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6160 - accuracy: 0.6576 - val_loss: 0.5550 - val_accuracy: 0.7132\n",
"Epoch 17/25\n",
"1351/1351 [==============================] - 2s 1ms/step - loss: 0.6132 - accuracy: 0.6589 - val_loss: 0.6529 - val_accuracy: 0.5999\n"
]
}
],
"source": [
"callback = keras.callbacks.EarlyStopping(monitor='val_loss', mode='min', patience=5, restore_best_weights=True)\n",
"model = create_model()\n",
"history = model.fit(train_x, train_y, validation_data=(valid_x, valid_y), epochs=25, callbacks=[callback])"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 12,
"outputs": [],
"source": [
"def create_model():\n",
" inputs = keras.Input(shape=(300,))\n",
" dense1 = layers.Dense(1024, activation=\"relu\")(inputs)\n",
" dense2 = layers.Dense(512, activation=\"relu\")(dense1)\n",
" dense3 = layers.Dense(128, activation=\"relu\")(dense2)\n",
" output = layers.Dense(1, activation=\"sigmoid\")(dense3)\n",
" model = keras.Model(inputs=inputs, outputs=output)\n",
" model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=1e-4), metrics=['accuracy'])\n",
" return model"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 14,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 1/25\n",
"1351/1351 [==============================] - 3s 2ms/step - loss: 0.6707 - accuracy: 0.5788 - val_loss: 0.6569 - val_accuracy: 0.5608\n",
"Epoch 2/25\n",
"1351/1351 [==============================] - 2s 2ms/step - loss: 0.6493 - accuracy: 0.6180 - val_loss: 0.8048 - val_accuracy: 0.3736\n",
"Epoch 3/25\n",
"1351/1351 [==============================] - 2s 2ms/step - loss: 0.6426 - accuracy: 0.6281 - val_loss: 0.6285 - val_accuracy: 0.6160\n",
"Epoch 4/25\n",
"1351/1351 [==============================] - 2s 2ms/step - loss: 0.6376 - accuracy: 0.6348 - val_loss: 0.6405 - val_accuracy: 0.5911\n",
"Epoch 5/25\n",
"1351/1351 [==============================] - 3s 2ms/step - loss: 0.6328 - accuracy: 0.6406 - val_loss: 0.6520 - val_accuracy: 0.5826\n",
"Epoch 6/25\n",
"1351/1351 [==============================] - 2s 2ms/step - loss: 0.6283 - accuracy: 0.6458 - val_loss: 0.6654 - val_accuracy: 0.5612\n",
"Epoch 7/25\n",
"1351/1351 [==============================] - 3s 2ms/step - loss: 0.6251 - accuracy: 0.6484 - val_loss: 0.6713 - val_accuracy: 0.5577\n",
"Epoch 8/25\n",
"1351/1351 [==============================] - 2s 2ms/step - loss: 0.6230 - accuracy: 0.6512 - val_loss: 0.5540 - val_accuracy: 0.7145\n",
"Epoch 9/25\n",
"1351/1351 [==============================] - 2s 2ms/step - loss: 0.6208 - accuracy: 0.6529 - val_loss: 0.6870 - val_accuracy: 0.5254\n",
"Epoch 10/25\n",
"1351/1351 [==============================] - 2s 2ms/step - loss: 0.6182 - accuracy: 0.6544 - val_loss: 0.5915 - val_accuracy: 0.6618\n",
"Epoch 11/25\n",
"1351/1351 [==============================] - 3s 2ms/step - loss: 0.6161 - accuracy: 0.6565 - val_loss: 0.6600 - val_accuracy: 0.5738\n",
"Epoch 12/25\n",
"1351/1351 [==============================] - 3s 2ms/step - loss: 0.6139 - accuracy: 0.6587 - val_loss: 0.7102 - val_accuracy: 0.5054\n",
"Epoch 13/25\n",
"1351/1351 [==============================] - 2s 2ms/step - loss: 0.6130 - accuracy: 0.6582 - val_loss: 0.5946 - val_accuracy: 0.6568\n"
]
}
],
"source": [
"callback = keras.callbacks.EarlyStopping(monitor='val_loss', mode='min', patience=5, restore_best_weights=True)\n",
"model = create_model()\n",
"history = model.fit(train_x, train_y, validation_data=(valid_x, valid_y), epochs=25, callbacks=[callback])"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 30,
"outputs": [
{
"data": {
"text/plain": "<matplotlib.legend.Legend at 0x1b6a1ae2400>"
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": "<Figure size 640x480 with 1 Axes>",
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAkAAAAHHCAYAAABXx+fLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB/dklEQVR4nO3dd3xUVfo/8M+dycyk9w5plNB7iYSmUgIqC7oWFBFQ0WWxsjYsoKLyU1eWVVlRFNvK2r7WxaUYBKX3DoFQEkJ6Qnqfub8/TmaSISGkzMyd8nm/XvOamyl3nhkxeeac5zxHkmVZBhEREZELUSkdABEREZGtMQEiIiIil8MEiIiIiFwOEyAiIiJyOUyAiIiIyOUwASIiIiKXwwSIiIiIXA4TICIiInI5TICIyKrS0tLw4osv4siRI1d8jF6vxxtvvIEff/zRhpERkStjAkREVlNXV4fp06fj0KFD6NOnzxUf9/zzz+O9997DNddcc9Vz5uTk4NZbb0VQUBAkScLy5cstGHGDTz75BJIkYe/evRY71/nz5023XXvttbj22ms7fG4iah8mQER24uuvv4YkSfj++++b3DdgwABIkoTffvutyX3R0dFITEy0WByvvfYafvjhB4uc6/nnnwcArFmzBipV879ufvnlF3z44YdYt24dwsLCrnrOxx9/HOvXr8fChQvx+eefY9KkSRaJ1VFlZmbixRdfxMGDB61y/jVr1lgtySRSEhMgIjsxatQoAMDWrVvNbi8pKcHRo0fh5uaGbdu2md134cIFXLhwwfRcS7BUAlRWVgYvLy/8/PPP8PDwuOLjzpw5g7Vr16JHjx6tOu+mTZswdepUPPHEE7j77rvRs2fPDsdqbTNnzkRlZSViYmJMt23YsAEbNmzo8LkzMzPx0ksvMQEiaiM3pQMgIiEyMhJxcXFNEqAdO3ZAlmXcdtttTe4z/tzRBEiWZVRVVbWYqLSVt7c3Xnjhhas+7uGHH27TeXNzc+Hv79/OqJShVquhVqvNbtNqtYrEUlFRAU9PT0Vem8iecASIyI6MGjUKBw4cQGVlpem2bdu2oU+fPpg8eTJ27twJg8Fgdp8kSRg5ciQA4OOPP8b111+P0NBQ6HQ69O7dG++9916T14mNjcVNN92E9evXY+jQofDw8MD7778PSZJQXl6OTz/9FJIkQZIkzJ492/S8AwcOYPLkyfD19YW3tzfGjRuHnTt3mp27trYWL730Erp37w53d3cEBQVh1KhR2Lhxo9njTp48idtvvx0hISHw8PBAjx498Nxzz13xszHW0ciyjBUrVpjiA4AXX3zRdNzccxrX3hjf+9atWzF8+HC4u7ujS5cu+Oyzz6742kaXLl3C8OHD0blzZ6SkpLT6vXSkBmjjxo0YNWoU/P394e3tjR49euDZZ58FAGzevBnDhg0DAMyZM8f0mXzyySem1+jbty/27duHMWPGwNPT0/TcH3/8ETfeeCMiIyOh0+nQtWtXLFmyBHq93izGtWvXIi0tzXTu2NhY0+jeo48+2iTejIwMqNVqLF269KrvjUhJHAEisiOjRo3C559/jl27dpn+OG7btg2JiYlITExEcXExjh49iv79+5vu69mzJ4KCggAA7733Hvr06YM//elPcHNzw88//4y//vWvMBgMmD9/vtlrpaSk4M4778SDDz6IuXPnokePHvj8889x//33Y/jw4XjggQcAAF27dgUAHDt2DKNHj4avry+eeuopaDQavP/++7j22muxZcsWJCQkABDJyNKlS03nKSkpwd69e7F//35MmDABAHD48GGMHj0aGo0GDzzwAGJjY3HmzBn8/PPPePXVV5v9bMaMGYPPP/8cM2fOxIQJE3DPPfe0+3NOTU3Frbfeivvuuw+zZs3C6tWrMXv2bAwZMuSKxdr5+fmYMGECCgsLsWXLFtPn0p730lrHjh3DTTfdhP79++Pll1+GTqdDamqqaSq0V69eePnll7Fo0SI88MADGD16NACY1YQVFBRg8uTJmD59Ou6++25TndUnn3wCb29vLFiwAN7e3ti0aRMWLVqEkpISvPnmmwCA5557DsXFxcjIyMA//vEPAGJkz9vbGzfffDO++uorLFu2zGx06z//+Q9kWcaMGTM69N6JrE4mIrtx7NgxGYC8ZMkSWZZluba2Vvby8pI//fRTWZZlOSwsTF6xYoUsy7JcUlIiq9Vqee7cuabnV1RUNDlnUlKS3KVLF7PbYmJiZADyunXrmjzey8tLnjVrVpPbp02bJmu1WvnMmTOm2zIzM2UfHx95zJgxptsGDBgg33jjjS2+zzFjxsg+Pj5yWlqa2e0Gg6HF58myLAOQ58+fb3bb4sWL5eZ+nX388ccyAPncuXOm24zv/ffffzfdlpubK+t0Ovlvf/tbk+fu2bNHzsrKkvv06SN36dJFPn/+fJvfS3NxjB07Vh47dmyL7/Uf//iHDEDOy8u74mP27NkjA5A//vjjJveNHTtWBiCvXLmyyX3N/Vt58MEHZU9PT7mqqsp024033ijHxMQ0eez69etlAPL//vc/s9v79+9/1fdFZA84BUZkR3r16oWgoCBTbc+hQ4dQXl5u+kafmJho+va/Y8cO6PV6s/qfxjU8xcXFyM/Px9ixY3H27FkUFxebvVZcXBySkpJaFZder8eGDRswbdo0dOnSxXR7REQE7rrrLmzduhUlJSUAAH9/fxw7dgynT59u9lx5eXn4/fffce+99yI6Otrsvuamsayhd+/eptESAAgJCUGPHj1w9uzZJo/NyMjA2LFjUVtbi99//92skNna78VY6/Tjjz+aTX22hU6nw5w5c5rc3vjfSmlpKfLz8zF69GhUVFTg5MmTVz3v+PHjERkZiS+++MJ029GjR3H48GHcfffd7YqVyJaYABHZEUmSkJiYaKr12bZtG0JDQ9GtWzcA5gmQ8bpxArRt2zaMHz8eXl5e8Pf3R0hIiKnmo7kEqLXy8vJQUVHR7EqtXr16wWAw4MKFCwCAl19+GUVFRYiPj0e/fv3w5JNP4vDhw6bHG5OMvn37tvr1Le3yZAUAAgICcOnSpSa3z5w5E7m5udiyZQs6depkdp+138sdd9yBkSNH4v7770dYWBimT5+Or7/+uk3JUKdOnZotuD527Bhuvvlm+Pn5wdfXFyEhIabE5fJ/K81RqVSYMWMGfvjhB1RUVAAAvvjiC7i7u+O2225rdXxESmECRGRnRo0aheLiYhw5csRU/2OUmJiItLQ0XLx4EVu3bkVkZKRpRObMmTMYN24c8vPzsWzZMqxduxYbN27E448/DgBN/mhacsVXY2PGjMGZM2ewevVq9O3bFx9++CEGDx6MDz/80CqvB1x5tKVxQW9jl6/IMpJlucltt9xyC4qKivDPf/6z/QG2k4eHB37//Xf8+uuvmDlzJg4fPow77rgDEyZMuOJ7a+4clysqKsLYsWNx6NAhvPzyy/j555+xceNGvP766wCa/lu5knvuuQdlZWX44YcfIMsy1qxZg5tuugl+fn6tf5NECmERNJGdadwPaNu2bXjsscdM9w0ZMgQ6nQ6bN2/Grl27cMMNN5ju+/nnn1FdXY2ffvrJbISjueaJLWkumQgJCYGnp6fZyiejkydPQqVSISoqynRbYGAg5syZgzlz5qCsrAxjxozBiy++iPvvv9+UsB09erRNcbUkICAAgPjD3niJfFpaWofP/fDDD6Nbt25YtGgR/Pz88Mwzz5jus8Z7uZxKpcK4ceMwbtw4LFu2DK+99hqee+45/Pbbbxg/fny7pto2b96MgoICfPfddxgzZozp9nPnzjV5bEvn79u3LwYNGoQvvvgCnTt3Rnp6Ot555502x0OkBI4AEdmZoUOHwt3dHV988QUuXrxoNgKk0+kwePBgrFixAuXl5WbTX8ZRjcajGMXFxfj444/b9PpeXl4oKioyu02tVmPixIn48ccfzZZy5+TkYM2aNRg1ahR8fX0BiFVHjXl7e6Nbt26orq4GIJKpMWPGYPXq1UhPTzd7bHMjMK1hXJH1+++/m24zLue3hBdeeAFPPPEEFi5caNZWwBrvpbHCwsImtw0
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from matplotlib import pyplot as plt\n",
"plt.plot(history.history['loss'])\n",
"plt.plot(history.history['val_loss'])\n",
"plt.title('Wartość funkcji straty')\n",
"plt.ylabel('Strata')\n",
"plt.xlabel('Epoka')\n",
"plt.legend(['train', 'valid'], loc='upper left')"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 31,
"outputs": [
{
"data": {
"text/plain": "<matplotlib.legend.Legend at 0x1b6a1ab1b50>"
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": "<Figure size 640x480 with 1 Axes>",
"image/png": "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
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from matplotlib import pyplot as plt\n",
"plt.plot(history.history['accuracy'])\n",
"plt.plot(history.history['val_accuracy'])\n",
"plt.title('model accuracy')\n",
"plt.ylabel('accuracy')\n",
"plt.xlabel('epoch')\n",
"plt.legend(['train', 'valid'], loc='upper left')"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 15,
"outputs": [],
"source": [
"model.save(\"fasttext_model.keras\")"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
2024-06-02 18:01:06 +02:00
"### Pomimo kilku prób dostosowania parametrów model cechuje się stosunkowo niską jakością predykcji, pomimo tego że wygląda na przeuczony\n",
"\n",
"(skacząca wartość accuracy/loss dla zbioru walidacyjnego)"
2024-06-02 17:59:28 +02:00
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 18,
"outputs": [],
"source": [
"import tensorflow as tf\n",
"def test_review_text(sentence):\n",
" vectorized = fb_model.wv.get_sentence_vector(sentence)\n",
" reshaped = tf.reshape(vectorized,shape=(1,300))\n",
" #print(vectorized.shape)\n",
" score = float(model(reshaped))\n",
" score_rounded = round(score)\n",
" print(score)\n",
" if score_rounded==0:\n",
" print(\"Negative review\")\n",
" else:\n",
" print(\"Positive review\")"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 19,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.7820833921432495\n",
"Positive review\n"
]
}
],
"source": [
"test_review_text(\"A buggy, uninspired mess\")"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 20,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.44321733713150024\n",
"Negative review\n"
]
}
],
"source": [
"test_review_text(\"This game is bad\")"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 21,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.8973167538642883\n",
"Positive review\n"
]
}
],
"source": [
"test_review_text(\"This game destroyed my life\")"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 22,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.8987871408462524\n",
"Positive review\n"
]
}
],
"source": [
"test_review_text(\"Best game I've ever played\")"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 23,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.5772996544837952\n",
"Positive review\n"
]
}
],
"source": [
"test_review_text(\"Fun cooperative play with scalable difficulty. Rapid path to get into a game with friends or open public games. \")"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 24,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.6418458819389343\n",
"Positive review\n"
]
}
],
"source": [
"test_review_text(\"Deliriously buggy. Fun if/when it works properly. Wait and see if they actually QA the next few patches before you play.\")"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 25,
"outputs": [],
"source": [
"test[\"model_predictions\"] = model(np.stack(test[\"vectorized\"].values))"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 26,
"outputs": [],
"source": [
"test[\"model_predictions\"] = test[\"model_predictions\"].apply(lambda x : round(float(x)))"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 27,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Accuracy: 0.72\n",
"Precision: 0.89\n",
"Recall: 0.76\n",
"F1 Score: 0.82\n"
]
}
],
"source": [
"def get_metrics():\n",
" df = test\n",
" predictions = df[\"model_predictions\"].to_numpy()\n",
" true_values = df[\"review_score\"].to_numpy()\n",
" accuracy = np.sum(np.rint(predictions) == true_values)/len(true_values)\n",
" TN_count = len(df.query(\"`review_score`==0 and `model_predictions`==0\").index)\n",
" TP_count = len(df.query(\"`review_score`==1 and `model_predictions`==1\").index)\n",
" FP_count = len(df.query(\"`review_score`==0 and `model_predictions`==1\").index)\n",
" FN_count = len(df.query(\"`review_score`==1 and `model_predictions`==0\").index)\n",
" precision = TP_count/(TP_count+FP_count)\n",
" recall = TP_count/(TP_count+FN_count)\n",
" F1_score = (2*precision*recall)/(precision+recall)\n",
" print(f\"Accuracy: {accuracy:.2f}\")\n",
" print(f\"Precision: {precision:.2f}\")\n",
" print(f\"Recall: {recall:.2f}\")\n",
" print(f\"F1 Score: {F1_score:.2f}\")\n",
"get_metrics()"
],
"metadata": {
"collapsed": false
}
},
2024-06-02 18:03:42 +02:00
{
"cell_type": "markdown",
"source": [
"Możliwe iż model osiągnąłby lepsze wyniki gdyby embeddingi były wyuczone na samych recenzjach gier, a nie pochodziły z gotowych wektorów dla języka angielskiego."
],
"metadata": {
"collapsed": false
}
},
2024-06-02 17:59:28 +02:00
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [],
"metadata": {
"collapsed": false
}
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 0
}