{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "490" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import os\n", "import numpy as np\n", "data_folder = os.path.join(os.path.expanduser(\"~\"), \"Data\", \"websites\", \"textonly\")\n", "\n", "documents = [open(os.path.join(data_folder, filename)).read() for filename in os.listdir(data_folder)]\n", "len(documents)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Pretty printing has been turned OFF\n" ] } ], "source": [ "pprint([document[:100] for document in documents[:5]])" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "from sklearn.cluster import KMeans\n", "from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "from sklearn.pipeline import Pipeline\n", "\n", "n_clusters = 10\n", "\n", "pipeline = Pipeline([('feature_extraction', TfidfVectorizer(max_df=0.4)),\n", " ('clusterer', KMeans(n_clusters=n_clusters))\n", " ])" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Pipeline(steps=[('feature_extraction', TfidfVectorizer(analyzer='word', binary=False, charset=None,\n", " charset_error=None, decode_error='strict',\n", " dtype=, encoding='utf-8', input='content',\n", " lowercase=True, max_df=0.4, max_features=None, min_df=1,\n", " ngram_range=(... n_init=10,\n", " n_jobs=1, precompute_distances=True, random_state=None, tol=0.0001,\n", " verbose=0))])" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pipeline.fit(documents)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "labels = pipeline.predict(documents)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cluster 0 contains 1 samples\n", "Cluster 1 contains 2 samples\n", "Cluster 2 contains 439 samples\n", "Cluster 3 contains 1 samples\n", "Cluster 4 contains 2 samples\n", "Cluster 5 contains 3 samples\n", "Cluster 6 contains 27 samples\n", "Cluster 7 contains 2 samples\n", "Cluster 8 contains 12 samples\n", "Cluster 9 contains 1 samples\n" ] } ], "source": [ "from collections import Counter\n", "c = Counter(labels)\n", "for cluster_number in range(n_clusters):\n", " print(\"Cluster {} contains {} samples\".format(cluster_number, c[cluster_number]))" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c[0]" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "381.97317261989065" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pipeline.named_steps['clusterer'].inertia_" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "inertia_scores = []\n", "n_cluster_values = list(range(2, 20))\n", "for n_clusters in n_cluster_values:\n", " cur_inertia_scores = []\n", " X = TfidfVectorizer(max_df=0.4).fit_transform(documents)\n", " for i in range(30):\n", " km = KMeans(n_clusters=n_clusters).fit(X)\n", " cur_inertia_scores.append(km.inertia_)\n", " inertia_scores.append(cur_inertia_scores)\n", "inertia_scores = np.array(inertia_scores)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAACOEAAAR9CAYAAAAam8EUAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XuQr3dB3/H3OUEGCXdRAsglUZGbRQRBVOSQoDAgt4IF\nhZQQKlitRMSM0BmGr1LEOhVoZ8pMx3hH0NYWzA3vHCgXrSiIgFRlpnUS4JxIoVaDCYTtH7vAEZKc\n88tvd7/77L5eMzv7/J7z29/z+Sv7x77zPAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+9pp1burS7Ze36H6neovqt+ubnfCe19c/WX1\nweo7dnEjAAAAAAAAAABMcfgU33dB9YFqY+v1i9qMcO5V/d7W66r7Vk/b+v6Y6jUrXAMAAAAAAAAA\nAPatr6x+t3pkn78TzgerO20dn7H1ujbvgvOjJ/zsb1bftAsbAQAAAAAAAABgmlO5S82rqgurz5xw\n7k7Vsa3jY30+yLlLdcUJ77uiuuuaGwEAAAAAAAAAYE87WYTzndXx6t3VoRt4z0aff0zVDf07AAAA\nAAAAAADsWzc7yb9/c/WE6rHVLarbVL/c5t1vzqg+Wt25zVCn6srqbif8/FdunftCf1V91U1eDQAA\nAAAAAAAAO+tD1VfvxAc/orpk6/inqh/dOn5R9ZNbx/et3lPdvDpza8z13UHH3XEAgKUYswcAAJyi\nMXsAAMApGrMHAACcopX6lpPdCeeGPvwnq/9cPaf6X9U/2zr/ga3zH6g+XX3/qoMAAAAAAAAAAIBT\nI8wBAJZizB4AAHCKxuwBAACnaMweAABwilbqWw7v1AoAgH3i6OwBAACn6OjsAQAAp+jo7AEAAPuJ\nO+EAAAAAAAAAALCXuRMOAAAAAAAAAADsJhEOAAAAAAAAAACsSYQDAAAAAAAAAABrEuEAAAAAAAAA\nAMCaRDgAAAAAAAAAALAmEQ4AAAAAAAAAAKxJhAMAAAAAAAAAAGsS4QAAAAAAAAAAwJpEOAAAAAAA\nAAAAsCYRDgAAAAAAAAAArEmEAwAAAAAAAAAAaxLhAAAAAAAAAADAmkQ4AAAAAAAAAACwJhEOAAAA\nAAAAAACsSYQDAAAAAAAAAABrEuEAAAAAAAAAAMCaRDgAAAAAAAAAALAmEQ4AAAAAAAAAAKxJhAMA\nAAAAAAAAAGsS4QAAAAAAAAAAwJpEOAAAAAAAAAAAsCYRDgAAAAAAAAAArEmEAwAAAAAAAAAAaxLh\nAAAAAAAAAADAmkQ4AAAAAAAAAACwJhEOAAAAAAAAAACsSYQDAAAAAAAAAABrEuEAAAAAAAAAAMCa\nRDgAAAAAAAAAALAmEQ4AAAAAAAAAAKxJhAMAAAAAAAAAAGsS4QAAAAAAAAAAwJpEOAAAAAAAAAAA\nsCYRDgAAAAAAAAAArEmEAwAAAAAAAAAAaxLhAAAAAAAAAADAmkQ4AAAAAAAAAACwJhEOAAAAAAAA\nAACsSYQDAAAAAAAAAABrEuEAAAAAAAAAAMCaRDgAAAAAAAAAALAmEQ4AAAAAAAAAAKxJhAMAAAAA\nAAAAAGsS4QAAAAAAAAAAwJpEOAAAAAAAAAAAsCYRDgAAAAAAAAAArEmEAwAAAAAAAAAAaxLhAAAA\nAAAAAADAmkQ4AAAAAAAAAACwJhEOAAAAAAAAAACsSYQDAAAAAAAAAABrEuEAAAAAAAAAAMCaRDgA\nAAAAAAAAALAmEQ4AAAAAAAAAAKxJhAMAAAAAAAAAAGsS4QAAAAAAAAAAwJpEOAAAAAAAAAAAsCYR\nDgAAAAAAAAAArEmEAwAAAAAAAAAAaxLhAAAAAAAAAADAmkQ4AAAAAAAAAACwJhEOAAAAAAAAAACs\nSYQDAAAAAAAAAABrEuEAAAAAAAAAAMCaRDgAAAAAAAAAALAmEQ4AAAAAAAAAAKxJhAMAAAAAAAAA\nAGsS4QAAAAAAAAAAwJpEOAAAAAAAAAAAsCYRDgAAAAAAAAAArEmEAwAAAAAAAAAAaxLhAAAAAAAA\nAADAmkQ4AAAAAAAAAACwJhEOAAAAAAAAAACsSYQDAAAAAAAAAABrEuEAAAAAAAAAAMCaRDgAAAAA\nAAAAALAmEQ4AAAAAAAAAAKxJhAMAAAAAAAAAAGsS4QAAAAAAAAAAwJpEOAAAAAAAAAAAsCYRDgAA\nAAAAAAAArEmEAwAAAAAAAAAAaxLhAAAAAAAAAADAmkQ4AAAAAAAAAACwJhEOAAAAAAAAAACsSYQD\nAAAAAAAAAABrEuEAAAAAAAAAAMCaRDgAAAAAAAAAALAmEQ4AAAAAAAAAAKxJhAMAAAAAAAAAAGsS\n4QAAAAAAAAAAwJpEOAAAAAAAAAAAsCYRDgAAAAAAAAAArEmEAwAAAAAAAAAAaxLhAAAAAAAAAADA\n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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "from matplotlib import pyplot as plt\n", "\n", "inertia_means = np.mean(inertia_scores, axis=1)\n", "inertia_stderr = np.std(inertia_scores, axis=1)\n", "\n", "fig = plt.figure(figsize=(40,20))\n", "plt.errorbar(n_cluster_values, inertia_means, inertia_stderr, color='green')\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Pipeline(steps=[('feature_extraction', TfidfVectorizer(analyzer='word', binary=False, charset=None,\n", " charset_error=None, decode_error='strict',\n", " dtype=, encoding='utf-8', input='content',\n", " lowercase=True, max_df=0.4, max_features=None, min_df=1,\n", " ngram_range=(... n_init=10,\n", " n_jobs=1, precompute_distances=True, random_state=None, tol=0.0001,\n", " verbose=0))])" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "n_clusters = 6\n", "\n", "pipeline = Pipeline([('feature_extraction', TfidfVectorizer(max_df=0.4)),\n", " ('clusterer', KMeans(n_clusters=n_clusters))\n", " ])\n", "pipeline.fit(documents)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "labels = pipeline.predict(documents)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cluster 0 contains 21 samples\n", " Most important terms\n", " 1) korea (score: 0.1591)\n", " 2) korean (score: 0.1573)\n", " 3) south (score: 0.1336)\n", " 4) kim (score: 0.1088)\n", " 5) north (score: 0.1077)\n", "\n", "Cluster 1 contains 33 samples\n", " Most important terms\n", " 1) iran (score: 0.1047)\n", " 2) netanyahu (score: 0.0869)\n", " 3) nuclear (score: 0.0660)\n", " 4) he (score: 0.0615)\n", " 5) his (score: 0.0550)\n", "\n", "Cluster 2 contains 156 samples\n", " Most important terms\n", " 1) palestinian (score: 0.0196)\n", " 2) israel (score: 0.0138)\n", " 3) security (score: 0.0118)\n", " 4) bank (score: 0.0115)\n", " 5) its (score: 0.0103)\n", "\n", "Cluster 3 contains 31 samples\n", " Most important terms\n", " 1) browser (score: 0.2786)\n", " 2) able (score: 0.2429)\n", " 3) you (score: 0.2018)\n", " 4) css (score: 0.1925)\n", " 5) sheets (score: 0.1925)\n", "\n", "Cluster 4 contains 48 samples\n", " Most important terms\n", " 1) al (score: 0.0862)\n", " 2) isis (score: 0.0723)\n", " 3) islamic (score: 0.0664)\n", " 4) iraq (score: 0.0657)\n", " 5) state (score: 0.0580)\n", "\n", "Cluster 5 contains 201 samples\n", " Most important terms\n", " 1) he (score: 0.0448)\n", " 2) we (score: 0.0306)\n", " 3) they (score: 0.0294)\n", " 4) his (score: 0.0254)\n", " 5) who (score: 0.0253)\n", "\n" ] } ], "source": [ "c = Counter(labels)\n", "\n", "terms = pipeline.named_steps['feature_extraction'].get_feature_names()\n", "\n", "for cluster_number in range(n_clusters):\n", " print(\"Cluster {} contains {} samples\".format(cluster_number, c[cluster_number]))\n", " print(\" Most important terms\")\n", " centroid = pipeline.named_steps['clusterer'].cluster_centers_[cluster_number]\n", " most_important = centroid.argsort()\n", " for i in range(5):\n", " term_index = most_important[-(i+1)]\n", " print(\" {0}) {1} (score: {2:.4f})\".format(i+1, terms[term_index], centroid[term_index]))\n", " print()" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.031820420989154712" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.metrics import silhouette_score\n", "X = pipeline.named_steps['feature_extraction'].transform(documents)\n", "silhouette_score(X, labels)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "14327" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(terms)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "Y = pipeline.transform(documents) " ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "km = KMeans(n_clusters=n_clusters)\n", "labels = km.fit_predict(Y)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cluster 0 contains 89 samples\n", "Cluster 1 contains 65 samples\n", "Cluster 2 contains 7 samples\n", "Cluster 3 contains 24 samples\n", "Cluster 4 contains 290 samples\n", "Cluster 5 contains 15 samples\n" ] } ], "source": [ "c = Counter(labels)\n", "for cluster_number in range(n_clusters):\n", " print(\"Cluster {} contains {} samples\".format(cluster_number, c[cluster_number]))" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.56564461613141714" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "silhouette_score(Y, labels)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(490, 6)" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Evidence Accumulation Clustering" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "from scipy.sparse import csr_matrix\n", "\n", "\n", "def create_coassociation_matrix(labels):\n", " rows = []\n", " cols = []\n", " unique_labels = set(labels)\n", " for label in unique_labels:\n", " indices = np.where(labels == label)[0]\n", " for index1 in indices:\n", " for index2 in indices:\n", " rows.append(index1)\n", " cols.append(index2)\n", " data = np.ones((len(rows),))\n", " return csr_matrix((data, (rows, cols)), dtype='float')\n" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "C = create_coassociation_matrix(labels)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "<490x490 sparse matrix of type ''\n", "\twith 97096 stored elements in Compressed Sparse Row format>" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "((490, 490), 240100)" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C.shape, C.shape[0] * C.shape[1]" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.4043981674302374" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(C.nonzero()[0]) / (C.shape[0] * C.shape[1])" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "from scipy.sparse.csgraph import minimum_spanning_tree" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "mst = minimum_spanning_tree(C)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "<490x490 sparse matrix of type ''\n", "\twith 484 stored elements in Compressed Sparse Row format>" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mst" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "pipeline = Pipeline([('feature_extraction', TfidfVectorizer(max_df=0.4)),\n", " ('clusterer', KMeans(n_clusters=3))\n", " ])\n", "pipeline.fit(documents)\n", "labels2 = pipeline.predict(documents)\n", "C2 = create_coassociation_matrix(labels2)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[ 1. , 0.5, 0.5, ..., 0.5, 0.5, 0.5],\n", " [ 0.5, 1. , 1. , ..., 1. , 1. , 1. ],\n", " [ 0.5, 1. , 1. , ..., 1. , 1. , 1. ],\n", " ..., \n", " [ 0.5, 1. , 1. , ..., 1. , 1. , 1. ],\n", " [ 0.5, 1. , 1. , ..., 1. , 1. , 1. ],\n", " [ 0.5, 1. , 1. , ..., 1. , 1. , 1. ]])" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C_sum = (C + C2) / 2\n", "#C_sum.data = C_sum.data\n", "C_sum.todense()" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "<490x490 sparse matrix of type ''\n", "\twith 489 stored elements in Compressed Sparse Row format>" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mst = minimum_spanning_tree(-C_sum)\n", "mst" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "<490x490 sparse matrix of type ''\n", "\twith 481 stored elements in Compressed Sparse Row format>" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#mst.data[mst.data < 1] = 0\n", "mst.data[mst.data > -1] = 0\n", "mst.eliminate_zeros()\n", "mst" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "from scipy.sparse.csgraph import connected_components\n", "number_of_clusters, labels = connected_components(mst)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "\n", "from sklearn.base import BaseEstimator, ClusterMixin\n", "\n", "class EAC(BaseEstimator, ClusterMixin):\n", " def __init__(self, n_clusterings=10, cut_threshold=0.5, n_clusters_range=(3, 10)):\n", " self.n_clusterings = n_clusterings\n", " self.cut_threshold = cut_threshold\n", " self.n_clusters_range = n_clusters_range\n", " \n", " def fit(self, X, y=None):\n", " C = sum((create_coassociation_matrix(self._single_clustering(X))\n", " for i in range(self.n_clusterings)))\n", " mst = minimum_spanning_tree(-C)\n", " mst.data[mst.data > -self.cut_threshold] = 0\n", " mst.eliminate_zeros()\n", " self.n_components, self.labels_ = connected_components(mst)\n", " return self\n", " \n", " def _single_clustering(self, X):\n", " n_clusters = np.random.randint(*self.n_clusters_range)\n", " km = KMeans(n_clusters=n_clusters)\n", " return km.fit_predict(X)\n", " \n", " def fit_predict(self, X):\n", " self.fit(X)\n", " return self.labels_" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "pipeline = Pipeline([('feature_extraction', TfidfVectorizer(max_df=0.4)),\n", " ('clusterer', EAC())\n", " ])" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Pipeline(steps=[('feature_extraction', TfidfVectorizer(analyzer='word', binary=False, charset=None,\n", " charset_error=None, decode_error='strict',\n", " dtype=, encoding='utf-8', input='content',\n", " lowercase=True, max_df=0.4, max_features=None, min_df=1,\n", " ngram_range=(...ocabulary=None)), ('clusterer', EAC(cut_threshold=0.5, n_clusterings=10, n_clusters_range=(3, 10)))])" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pipeline.fit(documents)" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "labels = pipeline.named_steps['clusterer'].labels_" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "c = Counter(labels)" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Counter({0: 490})" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Online Learning" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "from sklearn.cluster import MiniBatchKMeans" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [], "source": [ "vec = TfidfVectorizer(max_df=0.4)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [], "source": [ "X = vec.fit_transform(documents)" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [], "source": [ "mbkm = MiniBatchKMeans(random_state=14, n_clusters=3)\n", "batch_size = 500\n", "\n", "indices = np.arange(0, X.shape[0])\n", "for iteration in range(100):\n", " sample = np.random.choice(indices, size=batch_size, replace=True)\n", " mbkm.partial_fit(X[sample[:batch_size]])" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [], "source": [ "mbkm = MiniBatchKMeans(random_state=14, n_clusters=3)\n", "batch_size = 10\n", "\n", "for iteration in range(int(X.shape[0] / batch_size)):\n", " start = batch_size * iteration\n", " end = batch_size * (iteration + 1)\n", " mbkm.partial_fit(X[start:end])" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "401.6913550000382" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "labels_mbkm = mbkm.predict(X)\n", "mbkm.inertia_" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "392.6561949236311" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "km = KMeans(random_state=14, n_clusters=3)\n", "labels_km = km.fit_predict(X)\n", "km.inertia_" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "from sklearn.metrics import adjusted_mutual_info_score, homogeneity_score\n", "from sklearn.metrics import mutual_info_score, v_measure_score" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.39531764243316064" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v_measure_score(labels_mbkm, labels_km)" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(490, 14327)" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X.shape" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([2, 2, 0, 2, 1, 2, 2, 0, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n", " 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n", " 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 0, 1, 2, 0, 2, 2, 2, 1, 2, 2, 2, 2, 2,\n", " 2, 2, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 0,\n", " 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 1, 0, 0, 2, 2, 2, 2, 2, 2,\n", " 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 0, 2, 1, 0, 0, 2, 2, 1,\n", " 2, 0, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,\n", " 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 0, 0, 2, 0, 2, 2, 0, 2,\n", " 2, 2, 2, 2, 0, 2, 0, 2, 2, 2, 0, 2, 2, 2, 2, 0, 0, 2, 2, 2, 0, 2, 2,\n", " 2, 1, 2, 0, 2, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 1, 2, 0,\n", " 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 0, 2, 0, 2, 0, 2, 2,\n", " 2, 2, 2, 0, 2, 2, 0, 2, 2, 2, 2, 2, 0, 2, 2, 0, 1, 2, 2, 2, 2, 2, 2,\n", " 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 2, 2, 2,\n", " 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 1, 0, 0, 1, 2, 2, 2, 2, 0, 2, 2, 2, 2,\n", " 2, 2, 0, 2, 1, 2, 2, 2, 2, 1, 2, 0, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2,\n", " 0, 2, 0, 2, 2, 0, 2, 2, 2, 1, 2, 2, 0, 2, 2, 1, 2, 2, 0, 0, 2, 2, 0,\n", " 2, 2, 2, 1, 2, 1, 2, 1, 2, 2, 0, 0, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0,\n", " 0, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2,\n", " 2, 2, 2, 2, 2, 2, 0, 0, 1, 2, 0, 1, 2, 2, 2, 2, 2, 2, 0, 0, 2, 0, 2,\n", " 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 0, 2, 2, 2, 0, 0, 1, 2, 2, 2,\n", " 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 0, 2,\n", " 2, 0, 0, 0, 2, 2, 0], dtype=int32)" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "labels_mbkm" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [], "source": [ "from sklearn.feature_extraction.text import HashingVectorizer" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [], "source": [ "class PartialFitPipeline(Pipeline):\n", " def partial_fit(self, X, y=None):\n", " Xt = X\n", " for name, transform in self.steps[:-1]:\n", " Xt = transform.transform(Xt)\n", " return self.steps[-1][1].partial_fit(Xt, y=y)" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [], "source": [ "pipeline = PartialFitPipeline([('feature_extraction', HashingVectorizer()),\n", " ('clusterer', MiniBatchKMeans(random_state=14, n_clusters=3))\n", " ])" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [], "source": [ "batch_size = 10\n", "\n", "for iteration in range(int(len(documents) / batch_size)):\n", " start = batch_size * iteration\n", " end = batch_size * (iteration + 1)\n", " pipeline.partial_fit(documents[start:end])" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 2, 2, 0, 1, 0, 2, 2, 2, 2, 1, 2, 2, 0, 2, 2, 0, 2, 0, 2, 2, 1, 2,\n", " 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2,\n", " 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 1, 2, 2, 2, 0, 2, 1, 2, 2, 2, 1, 2,\n", " 2, 2, 2, 1, 1, 1, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 0, 2, 2, 1, 0, 0, 2,\n", " 1, 2, 2, 2, 1, 0, 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 0, 2, 2, 0,\n", " 0, 2, 0, 2, 2, 0, 0, 2, 2, 2, 1, 0, 0, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1,\n", " 2, 2, 1, 2, 2, 2, 2, 2, 1, 2, 2, 0, 2, 2, 2, 0, 2, 0, 2, 2, 2, 2, 2,\n", " 2, 2, 2, 2, 0, 2, 2, 2, 2, 0, 2, 0, 1, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2,\n", " 2, 2, 2, 2, 2, 0, 2, 0, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n", " 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 2, 2, 2, 0, 0, 0, 1, 0, 2,\n", " 1, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 2, 2, 0, 2, 0, 2, 2, 2,\n", " 0, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 0, 2, 2, 0,\n", " 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 0, 2, 2, 2, 2,\n", " 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n", " 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n", " 2, 1, 2, 0, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2,\n", " 0, 2, 2, 1, 0, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2,\n", " 2, 2, 2, 1, 2, 2, 2, 0, 0, 0, 2, 0, 2, 2, 0, 0, 2, 1, 2, 2, 2, 2, 2,\n", " 0, 2, 0, 2, 0, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2,\n", " 0, 2, 0, 2, 2, 2, 2, 1, 0, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2,\n", " 2, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n", " 0, 2, 2, 2, 2, 2, 2], dtype=int32)" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "labels = pipeline.predict(documents)\n", "labels" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "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.5.2" }, "toc": { "colors": { "hover_highlight": "#DAA520", "navigate_num": "#000000", "navigate_text": "#333333", "running_highlight": "#FF0000", "selected_highlight": "#FFD700", "sidebar_border": "#EEEEEE", "wrapper_background": "#FFFFFF" }, "moveMenuLeft": true, "nav_menu": { "height": "79px", "width": "253px" }, "navigate_menu": true, "number_sections": true, "sideBar": true, "threshold": 4, "toc_cell": false, "toc_section_display": "block", "toc_window_display": false, "widenNotebook": false } }, "nbformat": 4, "nbformat_minor": 1 }