{"cells": [{"cell_type": "markdown", "metadata": {"_uuid": "281275d20b3c5f659236d880031e3da4b7977e06"}, "source": ["# Import Data"]}, {"cell_type": "code", "execution_count": 1, "metadata": {"_uuid": "ebb7ec8cdfe9b840f5b5ba1524f4e892994e46cc"}, "outputs": [], "source": ["import pandas as pd\n", "import numpy as np\n", "import os\n", "\n", "import gc"]}, {"cell_type": "code", "execution_count": 2, "metadata": {"_uuid": "713132d2f045501c82e42290550d356ae0727c35"}, "outputs": [], "source": ["DATA_ROOT = '../input/'\n", "ORIGINAL_DATA_FOLDER = os.path.join(DATA_ROOT, 'movie-review-sentiment-analysis-kernels-only')\n", "TMP_DATA_FOLDER = os.path.join(DATA_ROOT, 'kaggle_review_sentiment_tmp_data')"]}, {"cell_type": "code", "execution_count": 3, "metadata": {"_uuid": "98fd884ce5265326d434498bda1f5bb813f24e95"}, "outputs": [], "source": ["train_data_path = os.path.join(ORIGINAL_DATA_FOLDER, 'train.tsv')\n", "test_data_path = os.path.join(ORIGINAL_DATA_FOLDER, 'test.tsv')\n", "sub_data_path = os.path.join(ORIGINAL_DATA_FOLDER, 'sampleSubmission.csv')\n", "\n", "train_df = pd.read_csv(train_data_path, sep=\"\\t\")\n", "test_df = pd.read_csv(test_data_path, sep=\"\\t\")\n", "sub_df = pd.read_csv(sub_data_path, sep=\",\")"]}, {"cell_type": "markdown", "metadata": {"_uuid": "ee07dfaceeb2d800b4c95430a5f44d7386fed119"}, "source": ["# EDA"]}, {"cell_type": "code", "execution_count": 4, "metadata": {"_uuid": "abc33cf6801467c6c8ccd5266961d5e74a70b030"}, "outputs": [], "source": ["import seaborn as sns\n", "from sklearn.feature_extraction import text as sktext"]}, {"cell_type": "code", "execution_count": 5, "metadata": {"_uuid": "33485dab7ac4678cc77fed13b2c8e500d341f72f"}, "outputs": [], "source": ["train_df.head()"]}, {"cell_type": "code", "execution_count": 6, "metadata": {"_uuid": "c2e0e484aacbb21ebb2807c79beca5aa054c3830"}, "outputs": [], "source": ["test_df.head()"]}, {"cell_type": "code", "execution_count": 7, "metadata": {"_uuid": "641a74568482c8ad42d1ac1c8982a44b84641166"}, "outputs": [], "source": ["sub_df.head()"]}, {"cell_type": "markdown", "metadata": {"_uuid": "c4d3c60c638b8ed36a90102e2f5d0fcb034b9ac4"}, "source": ["## Find Overlapped Phrases Between Train and Test Data"]}, {"cell_type": "code", "execution_count": 8, "metadata": {"_uuid": "0a31536ca50b348303c4980a7535fdea69716d6e"}, "outputs": [], "source": ["overlapped = pd.merge(train_df[[\"Phrase\", \"Sentiment\"]], test_df, on=\"Phrase\", how=\"inner\")\n", "overlap_boolean_mask_test = test_df['Phrase'].isin(overlapped['Phrase'])"]}, {"cell_type": "markdown", "metadata": {"_uuid": "22c203439a2310d54f0283dc4e117307a1afa82d"}, "source": ["## Explore Sentence Id"]}, {"cell_type": "code", "execution_count": 9, "metadata": {"_uuid": "c957e257b2865c258a6d9ce23b9ed15c316b90b8"}, "outputs": [], "source": ["print(\"training and testing data sentences hist:\")\n", "sns.distplot(train_df['SentenceId'], kde_kws={\"label\": \"train\"})\n", "sns.distplot(test_df['SentenceId'], kde_kws={\"label\": \"test\"})"]}, {"cell_type": "code", "execution_count": 10, "metadata": {"_uuid": "6ae6308dd0bb586f7cc6e75619e228f7984116ab"}, "outputs": [], "source": ["print(\"The number of overlapped SentenceId between training and testing data:\")\n", "train_overlapped_sentence_id_df = train_df[train_df['SentenceId'].isin(test_df['SentenceId'])]\n", "print(train_overlapped_sentence_id_df.shape[0])\n", "\n", "del train_overlapped_sentence_id_df\n", "gc.collect()"]}, {"cell_type": "code", "execution_count": 11, "metadata": {"_kg_hide-output": true, "_uuid": "030ab4a34391188a30160731259e21d4611762a2"}, "outputs": [], "source": ["pd.options.display.max_colwidth = 250\n", "print(\"Example of sentence and phrases: \")\n", "\n", "sample_sentence_id = train_df.sample(1)['SentenceId'].values[0]\n", "sample_sentence_group_df = train_df[train_df['SentenceId'] == sample_sentence_id]\n", "sample_sentence_group_df"]}, {"cell_type": "markdown", "metadata": {}, "source": ["There is no overlapped sentence between training and testing data. Within each sentence group, the phraseId order is the in-order tanversal over the dependency parsing tree of the sentence text. (This might be a very important information as we can utilized the composition as powerful predictive information)."]}, {"cell_type": "markdown", "metadata": {"_uuid": "09c94783b2b9f99c5d62ea251391642329ec46a6"}, "source": ["# Data Preprocessing"]}, {"cell_type": "code", "execution_count": 12, "metadata": {"_uuid": "f15786c0c632835ae7a008114319badd92dce4c8"}, "outputs": [], "source": ["from keras.preprocessing import sequence\n", "import gensim\n", "from sklearn import preprocessing as skp"]}, {"cell_type": "code", "execution_count": 13, "metadata": {"_uuid": "b50c845cca35a18a4f2986d03b37eb826c17e109"}, "outputs": [], "source": ["max_len = 50\n", "embed_size = 300\n", "max_features = 30000"]}, {"cell_type": "markdown", "metadata": {"_uuid": "5cd1af62c09ef764aed364830a0d2485ec9bb9af"}, "source": ["## Clean Texts"]}, {"cell_type": "markdown", "metadata": {"_uuid": "506b2f699332c271c1f5250286b038a3f01fb9c9"}, "source": ["## Reconstruct the Parsing Trees"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The training and testing do not contain complete parsing trees. So we assume missing nodes have sentiment score 2."]}, {"cell_type": "code", "execution_count": 14, "metadata": {"_uuid": "03a441ee0299bc153f899f2f02b0608531366d7f"}, "outputs": [], "source": ["class TreeNode:\n", "    \n", "    def __init__(self, left=None, right=None, phrase_id=None, phrase=None, sentiment=None):\n", "        self.left = left\n", "        self.right = right\n", "        self.phrase_id = phrase_id\n", "        self.sentiment = sentiment\n", "        self.phrase = phrase\n", "        \n", "    @classmethod\n", "    def build_preorder_tree(cls, df):\n", "        phrase_ids = df['PhraseId'].values.tolist()\n", "        phrases = df['Phrase'].values.tolist()\n", "        \n", "        if 'Sentiment' in df.columns:\n", "            sentiments = df['Sentiment'].values.tolist()\n", "        else:\n", "            sentiments = None\n", "        \n", "        return TreeNode.__build_preorder_tree(phrases, phrase_ids, sentiments, 0, len(phrases)-1)\n", "        \n", "    @classmethod\n", "    def __build_preorder_tree(cls, phrases, phrase_ids, sentiments, lo, hi):\n", "        if lo > hi:\n", "            return None\n", "        root = TreeNode(\n", "            phrase_id=phrase_ids[lo], phrase=phrases[lo].lower(), \n", "            sentiment=(2 if sentiments is None else sentiments[lo])\n", "        )\n", "        if lo == hi:\n", "            root = TreeNode.__split_double_node(root)\n", "            return root\n", "        \n", "        left_lo = lo + 1\n", "        \n", "        right_lo = lo + 2\n", "        while(right_lo < len(phrases) and phrases[right_lo].lower() in phrases[left_lo].lower()):\n", "            right_lo += 1\n", "        \n", "        root.left = TreeNode.__build_preorder_tree(phrases, phrase_ids, sentiments, left_lo, right_lo - 1)\n", "        root.right = TreeNode.__build_preorder_tree(phrases, phrase_ids, sentiments, right_lo, hi)\n", "        \n", "        if root.left is not None and root.right is None:\n", "            if root.phrase.startswith(root.left.phrase):\n", "                end_index = root.phrase.rindex(root.left.phrase) + len(root.left.phrase)\n", "                root.right = TreeNode(\n", "                    phrase_id=None, phrase=root.phrase[end_index:].strip(), \n", "                    sentiment=2\n", "                )\n", "                TreeNode.__split_double_node(root.right)\n", "            else:\n", "                start_index = root.phrase.find(root.left.phrase)\n", "                root.right = root.left\n", "                root.left = TreeNode(\n", "                    phrase_id=None, phrase=root.phrase[:start_index].strip(), \n", "                    sentiment=2\n", "                )\n", "                TreeNode.__split_double_node(root.left)\n", "                \n", "        return root\n", "    \n", "    @classmethod\n", "    def __split_double_node(cls, root):\n", "        splits = root.phrase.strip().split()\n", "        if len(splits) == 0:\n", "            return None\n", "        if len(splits) == 1:\n", "            return root\n", "        elif len(splits) >= 2 and len(splits) < 11:\n", "            root.left = TreeNode(\n", "                phrase_id=None, phrase=splits[0].lower(), \n", "                sentiment=2\n", "            )\n", "            root.right = TreeNode(\n", "                phrase_id=None, phrase=splits[1].lower(), \n", "                sentiment=2\n", "            )\n", "            return root\n", "        else:\n", "            raise ValueError(root.phrase)\n"]}, {"cell_type": "code", "execution_count": 15, "metadata": {"_uuid": "a73b6544720a253fa7ee41e28a20073ce5d67e76"}, "outputs": [], "source": ["def build_sent_id_tree_map(raw_df):\n", "    sent_id_tree_map = dict()\n", "    for sent_id in raw_df['SentenceId'].unique():\n", "        df = raw_df[raw_df['SentenceId'] == sent_id]\n", "        sent_id_tree_map[sent_id] = TreeNode.build_preorder_tree(df)\n", "    \n", "    return sent_id_tree_map\n", "\n", "train_trees = build_sent_id_tree_map(train_df)\n", "test_trees = build_sent_id_tree_map(test_df)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["### Flatten the Trees into Penn Treebank (PTB) Format"]}, {"cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": ["def ptb_flatten_tree(root):\n", "    if root.left is None and root.right is None:\n", "        return '(' + str(root.sentiment) + ' ' + root.phrase + ')'\n", "    \n", "    return '(' + str(root.sentiment) + ' ' + ptb_flatten_tree(root.left) + ' ' + ptb_flatten_tree(root.right) + ')'\n", "\n", "train_trees = dict([(sent_id, ptb_flatten_tree(tree)) for sent_id, tree in train_trees.items()])\n", "test_trees = dict([(sent_id, ptb_flatten_tree(tree)) for sent_id, tree in test_trees.items()])"]}, {"cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": ["train_trees_df = pd.Series(train_trees)\n", "print(train_df[train_df['SentenceId']==4054].iloc[0]['Phrase'])\n", "train_trees_df[4054]"]}, {"cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": ["print(train_df[train_df['Phrase']=='they are few and far between'])\n", "print(train_df[train_df['SentenceId']==899])"]}, {"cell_type": "markdown", "metadata": {"_uuid": "69e929bce14433c86cda53d35abc91a3b3fd4e72"}, "source": ["### Build Vocab"]}, {"cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": ["class Node:  # a node in the tree\n", "    def __init__(self, label, word=None):\n", "        self.label = label\n", "        self.word = word\n", "        self.parent = None  # reference to parent\n", "        self.left = None  # reference to left child\n", "        self.right = None  # reference to right child\n", "        # true if I am a leaf (could have probably derived this from if I have\n", "        # a word)\n", "        self.isLeaf = False\n", "        # true if we have finished performing fowardprop on this node (note,\n", "        # there are many ways to implement the recursion.. some might not\n", "        # require this flag)\n", "\n", "    def __str__(self):\n", "        if self.isLeaf:\n", "            return '[{0}:{1}]'.format(self.word, self.label)\n", "        return '({0} <- [{1}:{2}] -> {3})'.format(self.left, self.word, self.label, self.right)\n", "\n", "\n", "class Tree:\n", "\n", "    def __init__(self, treeString, openChar='(', closeChar=')'):\n", "        tokens = []\n", "        self.open = '('\n", "        self.close = ')'\n", "        for toks in treeString.strip().split():\n", "            tokens += list(toks)\n", "        self.root = self.parse(tokens)\n", "        # get list of labels as obtained through a post-order traversal\n", "        self.labels = get_labels(self.root)\n", "        self.num_words = len(self.labels)\n", "\n", "    def parse(self, tokens, parent=None):\n", "        assert tokens[0] == self.open, \"Malformed tree\"\n", "        assert tokens[-1] == self.close, \"Malformed tree\"\n", "\n", "        split = 2  # position after open and label\n", "        countOpen = countClose = 0\n", "\n", "        if tokens[split] == self.open:\n", "            countOpen += 1\n", "            split += 1\n", "        # Find where left child and right child split\n", "        while countOpen != countClose:\n", "            if tokens[split] == self.open:\n", "                countOpen += 1\n", "            if tokens[split] == self.close:\n", "                countClose += 1\n", "            split += 1\n", "\n", "        # New node\n", "        node = Node(int(tokens[1]))  # zero index labels\n", "\n", "        node.parent = parent\n", "\n", "        # leaf Node\n", "        if countOpen == 0:\n", "            node.word = ''.join(tokens[2: -1]).lower()  # lower case?\n", "            node.isLeaf = True\n", "            return node\n", "\n", "        node.left = self.parse(tokens[2: split], parent=node)\n", "        node.right = self.parse(tokens[split: -1], parent=node)\n", "\n", "        return node\n", "\n", "    def get_words(self):\n", "        leaves = getLeaves(self.root)\n", "        words = [node.word for node in leaves]\n", "        return words\n", "\n", "def get_labels(node):\n", "    if node is None:\n", "        return []\n", "    return get_labels(node.left) + get_labels(node.right) + [node.label]\n", "\n", "def getLeaves(node):\n", "    if node is None:\n", "        return []\n", "    if node.isLeaf:\n", "        return [node]\n", "    else:\n", "        return getLeaves(node.left) + getLeaves(node.right)\n", "\n", "    \n", "def loadTrees(trees_df):\n", "    \"\"\"\n", "    Loads training trees. Maps leaf node words to word ids.\n", "    \"\"\"\n", "    trees = [Tree(l) for l in trees_df.values.tolist()]\n", "\n", "    return trees"]}, {"cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": ["train_data = loadTrees(train_trees_df)"]}, {"cell_type": "code", "execution_count": 21, "metadata": {"_uuid": "f05a0bcf40751a8e33814fd3314b7f7e1d5149e5"}, "outputs": [], "source": ["flatten = lambda l: [item for sublist in l for item in sublist]\n", "vocab = list(set(flatten([t.get_words() for t in train_data])))\n", "\n", "word2index = {'<UNK>': 0}\n", "for vo in vocab:\n", "    if word2index.get(vo) is None:\n", "        word2index[vo] = len(word2index)\n", "        \n", "index2word = {v:k for k, v in word2index.items()}"]}, {"cell_type": "markdown", "metadata": {"_uuid": "4675184968f46ed87fbd703696978dc3535ce89e"}, "source": ["# Define Torch Model"]}, {"cell_type": "code", "execution_count": 23, "metadata": {"_uuid": "391b187f5f79d60fc329dd8a805708ac71937b5b"}, "outputs": [], "source": ["import torch\n", "import torch.nn as nn\n", "from torch.autograd import Variable\n", "import torch.optim as optim\n", "import torch.nn.functional as F"]}, {"cell_type": "markdown", "metadata": {"_uuid": "413a0fcd52787baf4cb702cc1074b8086675b1ba"}, "source": ["## Define Model"]}, {"cell_type": "code", "execution_count": 38, "metadata": {"_uuid": "e59457ba4f41bd4082188ccacff7f1cf8601545c"}, "outputs": [], "source": ["class RNTN(nn.Module):\n", "    \n", "    def __init__(self, word2index, hidden_size, output_size):\n", "        super(RNTN,self).__init__()\n", "        \n", "        self.word2index = word2index\n", "        self.embed = nn.Embedding(len(word2index), hidden_size)\n", "#         self.V = nn.ModuleList([nn.Linear(hidden_size*2,hidden_size*2) for _ in range(hidden_size)])\n", "#         self.W = nn.Linear(hidden_size*2,hidden_size)\n", "        self.V = nn.ParameterList([nn.Parameter(torch.randn(hidden_size * 2, hidden_size * 2)) for _ in range(hidden_size)]) # Tensor\n", "        self.W = nn.Parameter(torch.randn(hidden_size * 2, hidden_size))\n", "        self.b = nn.Parameter(torch.randn(1, hidden_size))\n", "#         self.W_out = nn.Parameter(torch.randn(hidden_size,output_size))\n", "        self.W_out = nn.Linear(hidden_size, output_size)\n", "        \n", "    def init_weight(self):\n", "        nn.init.xavier_uniform(self.embed.state_dict()['weight'])\n", "        nn.init.xavier_uniform(self.W_out.state_dict()['weight'])\n", "        for param in self.V.parameters():\n", "            nn.init.xavier_uniform(param)\n", "        nn.init.xavier_uniform(self.W)\n", "        self.b.data.fill_(0)\n", "#         nn.init.xavier_uniform(self.W_out)\n", "        \n", "    def tree_propagation(self, node):\n", "        \n", "        recursive_tensor = OrderedDict()\n", "        current = None\n", "        if node.isLeaf:\n", "            tensor = Variable(LongTensor([self.word2index[node.word]])) if node.word in self.word2index.keys() \\\n", "                          else Variable(LongTensor([self.word2index['<UNK>']]))\n", "            current = self.embed(tensor) # 1xD\n", "        else:\n", "            recursive_tensor.update(self.tree_propagation(node.left))\n", "            recursive_tensor.update(self.tree_propagation(node.right))\n", "            \n", "            concated = torch.cat([recursive_tensor[node.left], recursive_tensor[node.right]], 1) # 1x2D\n", "            xVx = [] \n", "            for i, v in enumerate(self.V):\n", "#                 xVx.append(torch.matmul(v(concated),concated.transpose(0,1)))\n", "                xVx.append(torch.matmul(torch.matmul(concated, v), concated.transpose(0, 1)))\n", "            \n", "            xVx = torch.cat(xVx, 1) # 1xD\n", "#             Wx = self.W(concated)\n", "            Wx = torch.matmul(concated, self.W) # 1xD\n", "\n", "            current = torch.tanh(xVx + Wx + self.b) # 1xD\n", "        recursive_tensor[node] = current\n", "        return recursive_tensor\n", "        \n", "    def forward(self, Trees, root_only=False):\n", "        \n", "        propagated = []\n", "        if not isinstance(Trees, list):\n", "            Trees = [Trees]\n", "            \n", "        for Tree in Trees:\n", "            recursive_tensor = self.tree_propagation(Tree.root)\n", "            if root_only:\n", "                recursive_tensor = recursive_tensor[Tree.root]\n", "                propagated.append(recursive_tensor)\n", "            else:\n", "                recursive_tensor = [tensor for node,tensor in recursive_tensor.items()]\n", "                propagated.extend(recursive_tensor)\n", "        \n", "        propagated = torch.cat(propagated) # (num_of_node in batch, D)\n", "        \n", "#         return F.log_softmax(propagated.matmul(self.W_out))\n", "        return F.log_softmax(self.W_out(propagated),1)"]}, {"cell_type": "markdown", "metadata": {"_uuid": "0f8a54b45eb4838d7459e6db57e9f0a95bfe7e60"}, "source": ["# Build and Train Models"]}, {"cell_type": "code", "execution_count": 35, "metadata": {"_uuid": "9dd9a276408dd3543d44e4e82cd8086024f56b3f"}, "outputs": [], "source": ["import matplotlib.pyplot as plt\n", "from IPython.display import SVG\n", "import random\n", "from collections import Counter, OrderedDict"]}, {"cell_type": "code", "execution_count": 28, "metadata": {"_uuid": "83515918a1861d9aa3ef7751aaf05ca1635d9620"}, "outputs": [], "source": ["HIDDEN_SIZE = 30\n", "ROOT_ONLY = False\n", "BATCH_SIZE = 20\n", "EPOCH = 20\n", "LR = 0.01\n", "LAMBDA = 1e-5\n", "RESCHEDULED = False\n", "USE_CUDA = True"]}, {"cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": ["model = RNTN(word2index, HIDDEN_SIZE,5)\n", "model.init_weight()\n", "if USE_CUDA:\n", "    model = model.cuda()\n", "\n", "loss_function = nn.CrossEntropyLoss()\n", "optimizer = optim.Adam(model.parameters(), lr=LR)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["FloatTensor = torch.cuda.FloatTensor if USE_CUDA else torch.FloatTensor\n", "LongTensor = torch.cuda.LongTensor if USE_CUDA else torch.LongTensor\n", "ByteTensor = torch.cuda.ByteTensor if USE_CUDA else torch.ByteTensor\n", "\n", "def getBatch(batch_size, train_data):\n", "    random.shuffle(train_data)\n", "    sindex = 0\n", "    eindex = batch_size\n", "    while eindex < len(train_data):\n", "        batch = train_data[sindex: eindex]\n", "        temp = eindex\n", "        eindex = eindex + batch_size\n", "        sindex = temp\n", "        yield batch\n", "    \n", "    if eindex >= len(train_data):\n", "        batch = train_data[sindex:]\n", "        yield batch\n", "\n", "for epoch in range(EPOCH):\n", "    losses = []\n", "    \n", "    # learning rate annealing\n", "    if RESCHEDULED == False and epoch == EPOCH//2:\n", "        LR *= 0.1\n", "        optimizer = optim.Adam(model.parameters(), lr=LR, weight_decay=LAMBDA) # L2 norm\n", "        RESCHEDULED = True\n", "    \n", "    for i, batch in enumerate(getBatch(BATCH_SIZE, train_data)):\n", "        \n", "        if ROOT_ONLY:\n", "            labels = [tree.labels[-1] for tree in batch]\n", "            labels = Variable(LongTensor(labels))\n", "        else:\n", "            labels = [tree.labels for tree in batch]\n", "            labels = Variable(LongTensor(flatten(labels)))\n", "        \n", "        model.zero_grad()\n", "        preds = model(batch, ROOT_ONLY)\n", "        \n", "        loss = loss_function(preds, labels)\n", "        losses.append(loss.data.tolist())\n", "        \n", "        loss.backward()\n", "        optimizer.step()\n", "        \n", "        if i % 100 == 0:\n", "            print('[%d/%d] mean_loss : %.2f' % (epoch, EPOCH, np.mean(losses)))\n", "            losses = []"]}, {"cell_type": "markdown", "metadata": {"_uuid": "d514af1525ce6dd02614bfdc4c1c707617eceade"}, "source": ["## Test"]}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "9fcdab9f1bfb6d88b2e3d110dea35f87c71dbf51"}, "outputs": [], "source": ["test_data = loadTrees(test_trees_df)"]}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "f5e58b1f129140130e4559b1610c307d50f15f8d"}, "outputs": [], "source": ["accuracy = 0\n", "num_node = 0"]}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "82b16272ecfe86ee0dc3e5731d4e33aa016842c3"}, "outputs": [], "source": ["for test in test_data:\n", "    model.zero_grad()\n", "    preds = model(test, ROOT_ONLY)\n", "    labels = test.labels[-1:] if ROOT_ONLY else test.labels\n", "    for pred, label in zip(preds.max(1)[1].data.tolist(), labels):\n", "        num_node += 1\n", "        if pred == label:\n", "            accuracy += 1\n", "\n", "print(accuracy/num_node * 100)"]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.8"}}, "nbformat": 4, "nbformat_minor": 1}