├── .gitignore ├── images ├── pyspark-shell.png ├── w2v-ibm-design.png ├── w2v-visual-via-pca.png ├── Top30WordToChristmasVis.png └── Top20ClosestWordsToChritstmas.png ├── ml-scripts ├── word2vecUtilities.pyc ├── README.md ├── word2vecUtilities.py ├── w2vAndKmeans.py ├── Word2Vec with Tweets.ipynb └── .ipynb_checkpoints │ └── Word2Vec with Tweets-checkpoint.ipynb ├── data └── filter.txt ├── LICENSE ├── Install Spark On Mac.txt ├── mllib-scripts ├── dist-to-words.py ├── cluster-words.py ├── visualize-words.py ├── README.md ├── tweets-to-w2v.py └── Word2Vec with Twitter Data using Spark RDDs.ipynb └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.npy 2 | *.gz 3 | -------------------------------------------------------------------------------- /images/pyspark-shell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/castanan/w2v/HEAD/images/pyspark-shell.png -------------------------------------------------------------------------------- /images/w2v-ibm-design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/castanan/w2v/HEAD/images/w2v-ibm-design.png -------------------------------------------------------------------------------- /images/w2v-visual-via-pca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/castanan/w2v/HEAD/images/w2v-visual-via-pca.png -------------------------------------------------------------------------------- /ml-scripts/word2vecUtilities.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/castanan/w2v/HEAD/ml-scripts/word2vecUtilities.pyc -------------------------------------------------------------------------------- /images/Top30WordToChristmasVis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/castanan/w2v/HEAD/images/Top30WordToChristmasVis.png -------------------------------------------------------------------------------- /images/Top20ClosestWordsToChritstmas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/castanan/w2v/HEAD/images/Top20ClosestWordsToChritstmas.png -------------------------------------------------------------------------------- /ml-scripts/README.md: -------------------------------------------------------------------------------- 1 | # These pyspark scripts use dataframe's and Spark ML and were added on March 2016 2 | 3 | 4 | # The pyspark scripts under under w2v/mllib-scripts use rdd's and Spark MLlib and were added on October 2015 5 | 6 | -------------------------------------------------------------------------------- /data/filter.txt: -------------------------------------------------------------------------------- 1 | santa 2 | claus 3 | merry 4 | christmas 5 | eve 6 | congrat 7 | happy 8 | holiday 9 | jingle 10 | bell 11 | silent 12 | night 13 | faith 14 | hope 15 | family 16 | new 17 | year 18 | spirit 19 | turkey 20 | ham 21 | food -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jorge Castanon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Install Spark On Mac.txt: -------------------------------------------------------------------------------- 1 | Instruction for downloading and installing Spark on an apple: 2 | 3 | 4 | 5 | - install JAVA for iOS from here http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 6 | 7 | 8 | 9 | - set JAVA_HOME to: 10 | 11 | export JAVA_HOME=$(/usr/libexec/java_home) 12 | 13 | 14 | 15 | - install home brew: 16 | 17 | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 18 | 19 | 20 | 21 | - install scala 22 | 23 | brew install scala 24 | 25 | 26 | 27 | - set SCALA_HOME 28 | 29 | export SCALA_HOME=/usr/local/bin/scala 30 | 31 | export PATH=$PATH:$SCALA_HOME/bin 32 | 33 | 34 | 35 | - Download Spark from https://spark.apache.org/downloads.html 36 | 37 | Choose the “Source Code” option 38 | 39 | 40 | 41 | - Untar and go to you Spark Home (or latest Spark version): 42 | 43 | tar -xvzf spark-1.5.1.tar 44 | 45 | cd spark-1.5.1 46 | 47 | 48 | 49 | - Install maven (mvn) with brew 50 | 51 | brew install maven 52 | 53 | 54 | 55 | - build spark with mavn 56 | 57 | mvn -DskipTests clean package 58 | 59 | 60 | 61 | - Open a Pyspark shell and print the spark context (sc): 62 | 63 | cd bin 64 | 65 | ./pyspark 66 | 67 | >>> sc 68 | 69 | 70 | 71 | 72 | 73 | Ready to Go! 74 | -------------------------------------------------------------------------------- /mllib-scripts/dist-to-words.py: -------------------------------------------------------------------------------- 1 | # Jorge Castanon, October 2015 2 | # Data Scientist @ IBM 3 | 4 | # run in terminal with the command 5 | # $ python mllib-scripts/dist-to-words.py 6 | 7 | import numpy as np 8 | from math import sqrt 9 | 10 | word = 'christmas' # word of interest 11 | nwords = 20 # number of words close to 'word' to print 12 | 13 | # Read the Word2Vec model, the list of words and the cluster labels 14 | Feat = np.load('mllib-scripts/myW2Vmatrix.npy') 15 | words = np.load('mllib-scripts/myWordList.npy') 16 | labels = np.load('mllib-scripts/myClusters.npy') 17 | 18 | Nw = words.shape[0] # total number of words 19 | ind_star = np.where(word == words) # find index associated to 'word' 20 | wstar = Feat[ind_star,:][0][0] # vector associated to 'word' 21 | nwstar = sqrt(np.dot(wstar,wstar)) # norm of vector assoicated with 'word' 22 | 23 | dist = np.zeros(Nw) # initialize vector of distances 24 | i = 0 25 | for w in Feat: # loop to compute cosine distances between 'word' and the rest of the words 26 | den = sqrt(np.dot(w,w))*nwstar # denominator of cosine distance 27 | dist[i] = abs( np.dot(wstar,w) )/den # cosine distance to each word 28 | i = i + 1 29 | 30 | indexes = np.argpartition(dist,-(nwords+1))[-(nwords+1):] 31 | di = [] 32 | for j in range(nwords+1): 33 | di.append(( words[indexes[j]], dist[indexes[j]], labels[indexes[j]] ) ) 34 | 35 | i = 0 36 | for elem in sorted(di,key=lambda x: x[1],reverse=True): 37 | if (i==1): 38 | print "Most Similar Words to", word,"and Cluster Labels:" 39 | print i, ".-", elem[0], elem[2] 40 | i = i + 1 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /mllib-scripts/cluster-words.py: -------------------------------------------------------------------------------- 1 | # Jorge Castanon, October 2015 2 | # Data Scientist @ IBM 3 | 4 | # run in terminal sitting on YOUR-PATH-TO-REPO: 5 | # ~/Documents/spark-1.5.1/bin/spark-submit mllib-scripts/cluster-words.py 6 | # Replace this line with: 7 | # /YOUR-SPARK-HOME/bin/spark-submit mllib-scripts/cluster-words.py 8 | 9 | import numpy as np 10 | import math 11 | 12 | from pyspark.context import SparkContext 13 | from pyspark.mllib.clustering import KMeans 14 | 15 | # next 2 lines can be reaplced to read from hdfs, 16 | # if the Word2Vec matrix is big 17 | Feat = np.load('mllib-scripts/myW2Vmatrix.npy') # reads model generated by Word2Vec 18 | words = np.load('mllib-scripts/myWordList.npy') # reads list of words 19 | 20 | print "\n=================================================" 21 | print "Size of the Word2Vec matrix is: ", Feat.shape 22 | print "Number of words in the models: ", words.shape 23 | print "=================================================\n" 24 | 25 | ## Spark Context 26 | sc = SparkContext('local','cluster-words') 27 | 28 | ## Read the Word2Vec model 29 | # the next line should be read/stored from hdfs if it is large 30 | Feat = sc.parallelize(Feat) 31 | 32 | ## K-means clustering with Spark 33 | K = int(math.floor(math.sqrt(float(words.shape[0])/2))) # Number of clusters 34 | # K ~ sqrt(n/2) this is a rule of thumb for choosing K, 35 | # where n is the number of words in the model 36 | # feel free to choose K with a fancier method 37 | maxiters = 100 # may change depending on the data 38 | clusters = KMeans.train(Feat, k = K, maxIterations = maxiters, runs = 10) 39 | 40 | print "\n=================================================" 41 | print "Number of clusters used: ", K 42 | print "=================================================\n" 43 | 44 | ## Getting Cluster Labels for each Word and saving to a numpy file 45 | labels = Feat.map(lambda point: clusters.predict(point)) # add labels to each vector (word) 46 | list_labels = labels.collect() 47 | np.save('mllib-scripts/myClusters.npy',list_labels) 48 | 49 | sc.stop() 50 | 51 | -------------------------------------------------------------------------------- /ml-scripts/word2vecUtilities.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | import pandas as pd 4 | import time 5 | import math 6 | from nltk.corpus import stopwords 7 | 8 | from pyspark import SparkContext 9 | from pyspark import Row 10 | from pyspark.sql import SQLContext 11 | from pyspark.ml.feature import Word2Vec 12 | from pyspark.ml.clustering import KMeans 13 | from pyspark.mllib.linalg import Vectors 14 | from pyspark.ml.feature import PCA 15 | 16 | import matplotlib.pyplot as plt 17 | from mpl_toolkits.mplot3d import Axes3D 18 | 19 | def topNwordsToPlot(dfComp,wordVectorsDF,word,nwords): 20 | 21 | compX = np.asarray(dfComp.map(lambda vec: vec[0][0]).collect()) 22 | compY = np.asarray(dfComp.map(lambda vec: vec[0][1]).collect()) 23 | compZ = np.asarray(dfComp.map(lambda vec: vec[0][2]).collect()) 24 | 25 | words = np.asarray(wordVectorsDF.select('word').toPandas().values.tolist()) 26 | Feat = np.asarray(wordVectorsDF.select('vector').rdd.map(lambda v: np.asarray(v[0])).collect()) 27 | 28 | Nw = words.shape[0] # total number of words 29 | ind_star = np.where(word == words) # find index associated to 'word' 30 | wstar = Feat[ind_star,:][0][0] # vector associated to 'word' 31 | nwstar = math.sqrt(np.dot(wstar,wstar)) # norm of vector assoicated with 'word' 32 | 33 | dist = np.zeros(Nw) # initialize vector of distances 34 | i = 0 35 | for w in Feat: # loop to compute cosine distances between 'word' and the rest of the words 36 | den = math.sqrt(np.dot(w,w))*nwstar # denominator of cosine distance 37 | dist[i] = abs( np.dot(wstar,w) )/den # cosine distance to each word 38 | i = i + 1 39 | 40 | indexes = np.argpartition(dist,-(nwords+1))[-(nwords+1):] 41 | di = [] 42 | for j in range(nwords+1): 43 | di.append(( words[indexes[j]], dist[indexes[j]], compX[indexes[j]], compY[indexes[j]], compZ[indexes[j]] ) ) 44 | 45 | result=[] 46 | for elem in sorted(di,key=lambda x: x[1],reverse=True): 47 | result.append((elem[0][0], elem[2], elem[3], elem[4])) 48 | 49 | return pd.DataFrame(result,columns=['word','X','Y','Z']) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spark-based machine learning for capturing word meanings 2 | 3 | In this repo, you will find out how to build Word2Vec models with Twitter data. For an end to end tutorial on how to build models on IBM's [Watson Studio](http://datascience.ibm.com/), please chech [this repo](https://github.com/IBMDataScience/word2vec). 4 | 5 | ## Pre-reqs: install Python, numpy and Apache Spark 6 | 7 | I.) Installing Anaconda installs Python, numpy, among other Python packages. If interested go here https://www.continuum.io/downloads 8 | 9 | II.) Download and Install Apache Spark go here: http://spark.apache.org/downloads.html 10 | 11 | This steps were useful for me to install Spark 1.5.1 on a Mac https://github.com/castanan/w2v/blob/master/Install%20Spark%20On%20Mac.txt 12 | 13 | III.) Added a notebook here https://github.com/castanan/w2v/blob/master/mllib-scripts/Word2Vec with Twitter Data usign Spark RDDs.ipynb 14 | and the good news are that Spark comes with Jupyter + Pyspark integrated. This notebook can be invoked from the shell by typing the command: 15 | IPYTHON_OPTS="notebook" ./bin/pyspark 16 | if you are sitting on YOUR-SPARK-HOME. 17 | 18 | ## Make sure that your pyspark is working 19 | 20 | I.) Go to your spark home directory 21 | 22 | cd YOUR-SPARK-HOME/bin 23 | 24 | II.) Open a pyspark shell by typing the command 25 | 26 | ./pyspark 27 | 28 | or Pyspark with Jupyter by typing the command 29 | 30 | IPYTHON_OPTS="notebook" ./bin/pyspark 31 | 32 | III.) print your spark context by typing sc in the pyspark shell, you should get something like this: 33 | 34 | ![image of pyspark shell] 35 | (https://github.com/castanan/w2v/blob/master/images/pyspark-shell.png) 36 | 37 | ## Get the Repo 38 | 39 | git clone https://github.com/castanan/w2v.git 40 | 41 | cd /YOUR-PATH-TO-REPO/w2v 42 | 43 | ## Get the Data 44 | 45 | Download (without uncompressing) some tweets from [here](https://ibm.box.com/s/mn5cenc1m6vuqm8qdwf2ddzuc4jyvpd4). The `tweets.gz` file contains a 10% sample (using Twitter decahose API) of a 15 minute batch of the public tweets from December 23rd. The size of this compressed file is 116MB (compression ratio is about 10 to 1). 46 | 47 | Note: there is no need to uncompress the file, just download the tweets.gz file and save it on the repo /YOUR-PATH-TO-REPO/w2v/data/. 48 | 49 | ## There are 2 options to perform the Twitter analysis: 50 | 51 | 1) (suggested) use dataframes and Spark ML (March 2016). see ml-scripts/README.md 52 | 53 | 2) use rdd's and Spark MLlib (October 2015). see mllib-scripts/README.md 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /mllib-scripts/visualize-words.py: -------------------------------------------------------------------------------- 1 | # Jorge Castanon, February 2016 2 | # Data Scientist @ IBM 3 | 4 | # run in terminal with: 5 | # ~/Documents/spark-1.5.1/bin/spark-submit mllib-scripts/visualize-words.py 6 | # Replace this line with: 7 | # /YOUR-SPARK-HOME/bin/spark-submit mllib-scripts/visualize-words.py 8 | 9 | import numpy as np 10 | import math 11 | 12 | from pyspark.context import SparkContext 13 | 14 | # next 2 lines can be replaced to read from hdfs, 15 | # if the Word2Vec matrix is big 16 | Feat = np.load('mllib-scripts/myW2Vmatrix.npy') # reads model generated by Word2Vec 17 | words = np.load('mllib-scripts/myWordList.npy') # reads list of words 18 | wordToModel = 'christmas' 19 | maxWordsVis = 30 20 | 21 | Feat = np.load('models/w2v_may1_may19_june1_june11.npy') 22 | words = np.load('models/word_may1_may19_june1_june11.npy') 23 | wordToModel = 'data' 24 | maxWordsVis = 10 25 | 26 | print "\n=================================================" 27 | print "Size of the Word2Vec matrix is: ", Feat.shape 28 | print "Number of words in the models: ", words.shape 29 | print "=================================================\n" 30 | 31 | ## Spark Context 32 | sc = SparkContext('local','visualize-words') 33 | 34 | ## Read the Word2Vec model 35 | # the next line should be read/stored from hdfs if it is large 36 | Feat = sc.parallelize(Feat) 37 | 38 | # map feature matrix to spark vectors 39 | from pyspark.mllib.linalg import Vectors 40 | Feat = Feat.map(lambda vec: (Vectors.dense(vec),)) 41 | 42 | ## Define a df with feature matrix 43 | from pyspark.sql import SQLContext 44 | sqlContext = SQLContext(sc) 45 | dfFeat = sqlContext.createDataFrame(Feat,["features"]) 46 | dfFeat.printSchema() 47 | 48 | ## PCA to project Feature matrix to 2 dimensions 49 | from pyspark.ml.feature import PCA 50 | numComponents = 3 51 | pca = PCA(k=numComponents, inputCol="features", outputCol="pcaFeatures") 52 | model = pca.fit(dfFeat) 53 | dfComp = model.transform(dfFeat).select("pcaFeatures") 54 | # get the first two components to lists to be plotted 55 | 56 | compX = dfComp.map(lambda vec: vec[0][0]).take(maxWordsVis) 57 | compY = dfComp.map(lambda vec: vec[0][1]).take(maxWordsVis) 58 | compZ = dfComp.map(lambda vec: vec[0][2]).take(maxWordsVis) 59 | 60 | ## finish Spark session 61 | sc.stop() 62 | 63 | ## plot 64 | fs=20 #fontsize 65 | w = words[0:maxWordsVis] 66 | import matplotlib.pyplot as plt 67 | from mpl_toolkits.mplot3d import Axes3D 68 | fig = plt.figure() 69 | ax = fig.add_subplot(111, projection='3d') 70 | ax.scatter(compX, compY, compZ, color='red', s=100, marker='o', edgecolors='black') 71 | for i, txt in enumerate(w): 72 | ax.text(compX[i],compY[i],compZ[i], '%s' % (txt), size=fs-5, zorder=1, color='k') 73 | ax.set_xlabel('1st. Component', fontsize=fs) 74 | ax.set_ylabel('2nd. Component', fontsize=fs) 75 | ax.set_zlabel('3rd. Component', fontsize=fs) 76 | title = 'Top',str(maxWordsVis),'closest words to term "',wordToModel ,'" in Twitter' 77 | ax.set_title(' '.join(title), fontsize=fs) 78 | ax.grid(True) 79 | plt.show() 80 | 81 | -------------------------------------------------------------------------------- /mllib-scripts/README.md: -------------------------------------------------------------------------------- 1 | # These pyspark scripts use rdd's and Spark MLlib 2 | 3 | # The pyspark scripts under under w2v/ml-scripts use dataframe's and Spark ML and were added on March 2016 4 | 5 | 6 | ## Instructions: 7 | 8 | ## Edit 9 | 10 | Open mllib-scripts/tweets-to-w2v.py and replace the data path 11 | 12 | datapath = '/Users/jorgecastanon/Documents/github/w2v/data/tweets.gz' 13 | 14 | with your path: 15 | 16 | datapath = 'YOUR-PATH-TO-REPO/w2v/data/tweets.gz' 17 | 18 | ## Generate the Word2Vec Model 19 | 20 | Execute the following command sitting on YOUR-PATH-TO-REPO 21 | 22 | ~/Documents/spark-1.5.1/bin/spark-submit mllib-scripts/tweets-to-w2v.py data/filter.txt 23 | 24 | replacing with your Spark home path: 25 | 26 | YOUR-SPARK-HOME/bin/spark-submit mllib-scripts/tweets-to-w2v.py data/filter.txt 27 | 28 | Note: this step generates files mllib-scripts/myW2Vmatrix.npy and mllib-scripts/myWordList.npy that are needed for the next step, so you may want to check if they were generated 29 | 30 | ## Cluster Twitter Terms with K-means 31 | 32 | Execute the following command sitting on YOUR-PATH-TO-REPO 33 | 34 | ~/Documents/spark-1.5.1/bin/spark-submit mllib-scripts/cluster-words.py 35 | 36 | replace with 37 | 38 | YOUR-SPARK-HOME/bin/spark-submit mllib-scripts/cluster-words.py 39 | 40 | Note: this step generates file mllib-scripts/myCluster.npy, so you may want to check if it was generated 41 | 42 | We are ready to rock! 43 | 44 | ## Test you Word2Vec Matrix 45 | 46 | Execute the following command 47 | 48 | python mllib-scripts/dist-to-words.py 49 | 50 | Your output should look like the following: 51 | 52 | ![image of Top 20 closest words to chritmas] 53 | (https://github.com/castanan/w2v/blob/master/images/Top20ClosestWordsToChritstmas.png) 54 | 55 | ## Visualization of the Word2Vec Matrix via PCA 56 | 57 | Execute the following command 58 | 59 | ~/Documents/spark-1.5.1/bin/spark-submit mllib-scripts/visualize-words.py 60 | 61 | replace with 62 | 63 | YOUR-SPARK-HOME/bin/spark-submit visualize-words.py 64 | 65 | You should get a cool 3D plot like this one: 66 | 67 | ![image of w2v-visual-via-pca] 68 | (https://github.com/castanan/w2v/blob/master/images/Top30WordToChristmasVis.png) 69 | 70 | 71 | ## NOTES: 72 | 73 | a. All these scripts run in local mode and need to be change to cluster mode for running them with large data sets of tweets. These scripts are a good starting point to play and understand the machine learning models. Nevertheless, these scripts need further work to be transformed into production and scalable code. 74 | 75 | b. If your w2v matrix is too large, you may want to save it in hdfs, as well as all the large files (list of words, list of cluster labels,...). 76 | 77 | c. A different list of keywords in filter.txt file may be useful for different applications. 78 | 79 | d. Larger amount of tweets (or text documents) are needed to get an accurate Word2Vec model. 80 | 81 | e. The singular values of the Word2Vec matrix may be useful to choose the number of dimensions for each of the vectors associated with your word terms, #'s (hashtags) and @'s (Twitter handlers). -------------------------------------------------------------------------------- /ml-scripts/w2vAndKmeans.py: -------------------------------------------------------------------------------- 1 | # Jorge Castanon, March 2016 2 | # Data Scientist @ IBM 3 | 4 | # run in terminal with comamnd sitting on YOUR-PATH-TO-REPO: 5 | # ~/Documents/spark-1.5.1/bin/spark-submit ml-scripts/w2vAndKmeans.py 6 | # Replace this line with: 7 | # /YOUR-SPARK-HOME/bin/spark-submit ml-scripts/w2vAndKmeans.py 8 | 9 | import numpy as np 10 | import pandas as pd 11 | import time 12 | import math 13 | from nltk.corpus import stopwords 14 | 15 | from pyspark import SparkContext 16 | from pyspark import Row 17 | from pyspark.sql import SQLContext 18 | from pyspark.ml.feature import Word2Vec 19 | from pyspark.ml.clustering import KMeans 20 | 21 | ## Spark and sql contexts 22 | sc = SparkContext('local', 'train-w2v') #change to cluster mode when needed 23 | sqlContext = SQLContext(sc) 24 | 25 | datapath = '/Users/jorgecastanon/Documents/github/w2v/data/tweets.gz' 26 | # Replace this line with: 27 | # datapath = '/YOUR-PATH-TO-REPO/w2v/data/tweets.gz' 28 | 29 | ## Read Tweets 30 | t0 = time.time() 31 | tweets = sqlContext.read.json(datapath) 32 | tweets.registerTempTable("tweets") 33 | timeReadTweets = time.time() - t0 34 | 35 | ## Read Keywords from w2v/data/filter.txt 36 | filterPath = '/Users/jorgecastanon/Documents/github/w2v/data/filter.txt' 37 | filter = pd.read_csv(filterPath,header=None) 38 | 39 | ## Filter Tweets 40 | # construct SQL Command 41 | t0 = time.time() 42 | sqlString = "(" 43 | for substr in filter[0]: #iteration on the list of words to filter (at most 50-100 words) 44 | sqlString = sqlString+"text LIKE '%"+substr+"%' OR " 45 | sqlString = sqlString+"text LIKE '%"+substr.upper()+"%' OR " 46 | sqlString=sqlString[:-4]+")" 47 | sqlFilterCommand = "SELECT lang, text FROM tweets WHERE (lang = 'en') AND "+sqlString 48 | tweetsDF = sqlContext.sql(sqlFilterCommand) 49 | timeFilterTweets = time.time() - t0 50 | 51 | ## Parse and Remove Stop Words 52 | tweetsRDD = tweetsDF.select('text').rdd 53 | def parseAndRemoveStopWords(text): 54 | t = text[0].replace(";"," ").replace(":"," ").replace('"',' ') 55 | t = t.replace(',',' ').replace('.',' ').replace('-',' ') 56 | t = t.lower().split(" ") 57 | stop = stopwords.words('english') 58 | return [i for i in t if i not in stop] 59 | tw = tweetsRDD.map(parseAndRemoveStopWords) 60 | 61 | ## Train Word2Vec Model with Spark ML 62 | try: 63 | twDF = tw.map(lambda p: Row(text=p)).toDF() 64 | except: 65 | print "For some reason, the first time to run the last command trows an error. The Error dissapears the second time that the command is run" 66 | twDF = tw.map(lambda p: Row(text=p)).toDF() 67 | t0 = time.time() 68 | word2Vec = Word2Vec(vectorSize=100, minCount=5, stepSize=0.025, inputCol="text", outputCol="result") 69 | modelW2V = word2Vec.fit(twDF) 70 | wordVectorsDF = modelW2V.getVectors() 71 | timeW2V = time.time() - t0 72 | 73 | ## Train K-means on top of the Word2Vec matrix: 74 | t0 = time.time() 75 | vocabSize = wordVectorsDF.count() 76 | K = int(math.floor(math.sqrt(float(vocabSize)/2))) 77 | # K ~ sqrt(n/2) this is a rule of thumb for choosing K, 78 | # where n is the number of words in the model 79 | # feel free to choose K with a fancier algorithm 80 | dfW2V = wordVectorsDF.select('vector').withColumnRenamed('vector','features') 81 | kmeans = KMeans(k=K, seed=1) 82 | modelK = kmeans.fit(dfW2V) 83 | labelsDF = modelK.transform(dfW2V).select('prediction').withColumnRenamed('prediction','labels') 84 | vocabSize = wordVectorsDF.count() 85 | timeKmeans = time.time() - t0 86 | 87 | sc.stop() 88 | 89 | 90 | ## Print Some Results 91 | printResults = 1 # set t 92 | if (printResults): 93 | ## Read Tweets 94 | 95 | print "="*80 96 | print "Read Tweets..." 97 | print "Elapsed time (seconds) to read tweets as a data frame: ", timeReadTweets 98 | print "="*80 99 | 100 | ## Filter Tweets 101 | 102 | print "Filter Tweets..." 103 | print "Elapsed time (seconds) to filter tweets of interest: ", timeFilterTweets 104 | print "="*80 105 | 106 | ## Word2Vec 107 | 108 | print "Build Word2Vec Matrix..." 109 | print "Elapsed time (seconds) to build Word2Vec: ", timeW2V 110 | print "Vocabulary Size: ", vocabSize 111 | print "="*80 112 | 113 | ## Kmeans 114 | 115 | print "Train K-means clustering..." 116 | print "Elapsed time (seconds) training K-means: ", timeKmeans 117 | print "Number of Clusters: ", K 118 | print "="*80 119 | 120 | #save models: 121 | saveModels = 0 122 | if(saveModels): 123 | def toList(df,colName): 124 | dfCol = df.select(colName) 125 | return dfCol.map(lambda e: e[0]).collect() 126 | 127 | w2vMatrix = toList(wordVectorsDF,'vector') 128 | np.save('w2vMatrix.npy',w2vMatrix) 129 | 130 | words = toList(wordVectorsDF,'word') 131 | np.save('words.npy',words) 132 | 133 | lables = toList(labelsDF,'labels') 134 | np.save('labels.npy',words) 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /mllib-scripts/tweets-to-w2v.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Jorge Castanon, October 2015 4 | # Data Scientist @ IBM 5 | 6 | # run in terminal with comamnd sitting on YOUR-PATH-TO-REPO: 7 | # ~/Documents/spark-1.5.1/bin/spark-submit mllib-scripts/tweets-to-w2v.py data/filter.txt 8 | # Replace this line with: 9 | # /YOUR-SPARK-HOME/bin/spark-submit mllib-scripts/tweets-to-w2v.py data/filter.txt 10 | 11 | # filter.txt contains a list of words of interest and may vary 12 | # depending on the application. In this example, filter.txt contain 13 | # chritmas related words: santa, claus, rudolf,... 14 | 15 | import os, sys, codecs, json 16 | import numpy as np 17 | from math import sqrt 18 | 19 | from pyspark import SparkContext 20 | from pyspark.mllib.feature import Word2Vec 21 | 22 | # read the keywords to filter tweets 23 | # in our example, these keywords are stored in 'filter.txt' 24 | def readFilters(filterpath): 25 | filters = set() 26 | f = open(filterpath, 'r') 27 | for line in f: 28 | line = codecs.decode(line, "utf-8") 29 | line = line.strip() 30 | if len(line) == 0: continue 31 | filters.add(line.lower()) 32 | f.close() 33 | return filters 34 | 35 | # parse text of each tweets in english that contains at least one keyword 36 | def process(filters): 37 | def realProcess(line): 38 | key = 'text' 39 | try: 40 | t = json.loads(line) 41 | if key not in t or t['lang']!='en': return None 42 | value = t[key].lower() 43 | # match with filters 44 | found = False 45 | for ff in filters: 46 | if ff in value: 47 | return t 48 | break 49 | except Exception as e: 50 | return None 51 | return None 52 | return realProcess 53 | 54 | def main(filterpath): 55 | filters = readFilters(filterpath) 56 | print >>sys.stderr, "Loaded %d filters" % len(filters) 57 | 58 | ## spark context 59 | sc = SparkContext('local', 'train-w2v') #change to cluster mode when needed 60 | 61 | datapath = '/Users/jorgecastanon/Documents/github/w2v/data/tweets.gz' 62 | # Replace this line with: 63 | # datapath = '/YOUR-PATH-TO-REPO/w2v/data/tweets.gz' 64 | 65 | data = sc.textFile(datapath) 66 | totaltw = data.count() 67 | 68 | print "\n=================================================" 69 | print "Number of total tweets processed: ", totaltw 70 | print "=================================================\n" 71 | 72 | 73 | # the next line filters tweets of interest 74 | tweets = data.map(process(filters)).filter(lambda x: x != None).map(lambda t: t['text']) 75 | twcount = tweets.count() 76 | # the next line cleans unwanted characters and transform text to lower case 77 | tweets = tweets.map(lambda x: x.replace(";"," ").replace(":"," ").replace('"',' ').replace('-',' ').replace(',',' ').replace('.',' ').lower()) 78 | # the next line breaks tweets into words 79 | tweets = tweets.map(lambda row: row.split(" ")) 80 | 81 | 82 | print "\n=================================================" 83 | print "Number of filtered tweets used: ", twcount 84 | print "=================================================\n" 85 | 86 | ## train NN model with word2vec 87 | word2vec = Word2Vec() 88 | model = word2vec.fit(tweets) # complexity 89 | 90 | ## Get the list of words in the w2v matrix 91 | vocabsize = 10000 # max vocabulary size is at most 10K words 92 | any_word = 'christmas' # can be any word in your model 93 | # the next line finds the top 'vocabsize' words to 'any_word' 94 | tmp_list = model.findSynonyms(any_word, vocabsize-1) 95 | # the newt few lines define the list of words in the model 96 | list_words = [] 97 | for l in tmp_list: 98 | list_words.append(l[0]) 99 | list_words.append(any_word) 100 | 101 | nwords = len(list_words) 102 | nfeatures = model.transform(any_word).array.shape[0] 103 | 104 | print "\n=================================================" 105 | print "Number of words in the model:", nwords 106 | print "=================================================" 107 | print "Number of features per word: ", nfeatures 108 | print "=================================================\n" 109 | 110 | ## Construct the feature matrix, each row vector is associated to each word in 'list_words' 111 | feature_matrix = [] 112 | for word in list_words: 113 | # model.transform : word => vectors 114 | # this function maps a word to its numerical vector 115 | feature_matrix.append(model.transform(word).array) 116 | 117 | ## save W2V matrix and the list of words 118 | np.save('mllib-scripts/myW2Vmatrix.npy',feature_matrix) 119 | np.save('mllib-scripts/myWordList.npy',list_words) 120 | 121 | sc.stop() 122 | return 123 | print >> sys.stderr, cnt, cnt_out, cnt_err 124 | 125 | def checkInput(): 126 | if len(sys.argv) != 2: 127 | print "Usage: " + os.path.basename(sys.argv[0]) + " filter_file_path" 128 | sys.exit() 129 | return sys.argv[1] 130 | 131 | if __name__ == '__main__': 132 | filterpath = checkInput() 133 | main(filterpath) 134 | -------------------------------------------------------------------------------- /ml-scripts/Word2Vec with Tweets.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import pandas as pd\n", 13 | "import time\n", 14 | "import math\n", 15 | "from nltk.corpus import stopwords\n", 16 | "\n", 17 | "from pyspark import SparkContext\n", 18 | "from pyspark import Row\n", 19 | "from pyspark.sql import SQLContext\n", 20 | "from pyspark.ml.feature import Word2Vec\n", 21 | "from pyspark.ml.clustering import KMeans\n", 22 | "from pyspark.mllib.linalg import Vectors\n", 23 | "from pyspark.ml.feature import PCA\n", 24 | "\n", 25 | "import matplotlib.pyplot as plt\n", 26 | "from mpl_toolkits.mplot3d import Axes3D\n", 27 | "\n", 28 | "import word2vecUtilities as wvu" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "# Read Twitter Data as a Spark DataFrame" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 2, 41 | "metadata": { 42 | "collapsed": false 43 | }, 44 | "outputs": [ 45 | { 46 | "name": "stdout", 47 | "output_type": "stream", 48 | "text": [ 49 | "Number of tweets read: 239082\n", 50 | "Elapsed time (seconds): 33.6140749454\n" 51 | ] 52 | } 53 | ], 54 | "source": [ 55 | "t0 = time.time()\n", 56 | "datapath = '/Users/jorgecastanon/Documents/github/w2v/data/tweets.gz'\n", 57 | "tweets = sqlContext.read.json(datapath)\n", 58 | "tweets.registerTempTable(\"tweets\")\n", 59 | "twr = tweets.count()\n", 60 | "print \"Number of tweets read: \", twr \n", 61 | "# this line add ~7 seconds (from ~24.5 seconds to ~31.5 seconds)\n", 62 | "# Number of tweets read: 239082\n", 63 | "print \"Elapsed time (seconds): \", time.time() - t0\n", 64 | "#Elapsed time (seconds): 31.9646401405" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "# Read Keywords: christmas, santa, turkey, ..." 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 4, 77 | "metadata": { 78 | "collapsed": false 79 | }, 80 | "outputs": [ 81 | { 82 | "data": { 83 | "text/html": [ 84 | "
\n", 85 | "\n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | "
0
0santa
1claus
2merry
3christmas
4eve
\n", 115 | "
" 116 | ], 117 | "text/plain": [ 118 | " 0\n", 119 | "0 santa\n", 120 | "1 claus\n", 121 | "2 merry\n", 122 | "3 christmas\n", 123 | "4 eve" 124 | ] 125 | }, 126 | "execution_count": 4, 127 | "metadata": {}, 128 | "output_type": "execute_result" 129 | } 130 | ], 131 | "source": [ 132 | "filterPath = '/Users/jorgecastanon/Documents/github/w2v/data/filter.txt'\n", 133 | "filter = pd.read_csv(filterPath,header=None)\n", 134 | "filter.head()" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "# Use Spark SQL to Filter Tweets:\n", 142 | "### + In english\n", 143 | "### + And containing at least one keyword" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": 5, 149 | "metadata": { 150 | "collapsed": false 151 | }, 152 | "outputs": [ 153 | { 154 | "name": "stdout", 155 | "output_type": "stream", 156 | "text": [ 157 | "Number of tweets after filtering: 15999\n", 158 | "Elapsed time (seconds): 10.3279871941\n", 159 | "Percetage of Tweets Used: 0.0669184631214\n" 160 | ] 161 | } 162 | ], 163 | "source": [ 164 | "# Construct SQL Command\n", 165 | "t0 = time.time()\n", 166 | "sqlString = \"(\"\n", 167 | "for substr in filter[0]: #iteration on the list of words to filter (at most 50-100 words)\n", 168 | " sqlString = sqlString+\"text LIKE '%\"+substr+\"%' OR \"\n", 169 | " sqlString = sqlString+\"text LIKE '%\"+substr.upper()+\"%' OR \"\n", 170 | "sqlString=sqlString[:-4]+\")\"\n", 171 | "sqlFilterCommand = \"SELECT lang, text FROM tweets WHERE (lang = 'en') AND \"+sqlString\n", 172 | "\n", 173 | "# Query tweets in english that contain at least one of the keywords\n", 174 | "tweetsDF = sqlContext.sql(sqlFilterCommand).cache()\n", 175 | "twf = tweetsDF.count()\n", 176 | "print \"Number of tweets after filtering: \", twf \n", 177 | "# last line add ~9 seconds (from ~0.72 seconds to ~9.42 seconds)\n", 178 | "print \"Elapsed time (seconds): \", time.time() - t0\n", 179 | "\n", 180 | "print \"Percetage of Tweets Used: \", float(twf)/twr" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": {}, 186 | "source": [ 187 | "# Parse Tweets and Remove Stop Words " 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": 6, 193 | "metadata": { 194 | "collapsed": false 195 | }, 196 | "outputs": [], 197 | "source": [ 198 | "tweetsRDD = tweetsDF.select('text').rdd\n", 199 | "\n", 200 | "def parseAndRemoveStopWords(text):\n", 201 | " t = text[0].replace(\";\",\" \").replace(\":\",\" \").replace('\"',' ').replace('-',' ')\n", 202 | " t = t.replace(',',' ').replace('.',' ')\n", 203 | " t = t.lower().split(\" \")\n", 204 | " stop = stopwords.words('english')\n", 205 | " return [i for i in t if i not in stop]\n", 206 | "\n", 207 | "tw = tweetsRDD.map(parseAndRemoveStopWords)" 208 | ] 209 | }, 210 | { 211 | "cell_type": "markdown", 212 | "metadata": {}, 213 | "source": [ 214 | "# Word2Vec: returns a dataframe with words and vectors\n", 215 | "\n", 216 | "+ Sometimes you need to run this block twice (strange reason that need to de-bug)" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 8, 222 | "metadata": { 223 | "collapsed": false 224 | }, 225 | "outputs": [ 226 | { 227 | "name": "stdout", 228 | "output_type": "stream", 229 | "text": [ 230 | "Elapsed time (seconds) to train Word2Vec: 8.02205491066\n" 231 | ] 232 | } 233 | ], 234 | "source": [ 235 | "# map to df\n", 236 | "twDF = tw.map(lambda p: Row(text=p)).toDF()\n", 237 | "\n", 238 | "# default minCount = 5 (we may need to try something larger: 20-100 to reduce cost)\n", 239 | "# default vectorSize = 100 (we may want to keep default)\n", 240 | "t0 = time.time()\n", 241 | "word2Vec = Word2Vec(vectorSize=100, minCount=5, inputCol=\"text\", outputCol=\"result\")\n", 242 | "modelW2V = word2Vec.fit(twDF)\n", 243 | "wordVectorsDF = modelW2V.getVectors()\n", 244 | "print \"Elapsed time (seconds) to train Word2Vec: \", time.time() - t0" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 9, 250 | "metadata": { 251 | "collapsed": false 252 | }, 253 | "outputs": [ 254 | { 255 | "name": "stdout", 256 | "output_type": "stream", 257 | "text": [ 258 | "1.5.1\n" 259 | ] 260 | } 261 | ], 262 | "source": [ 263 | "print sc.version" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": 10, 269 | "metadata": { 270 | "collapsed": false 271 | }, 272 | "outputs": [ 273 | { 274 | "name": "stdout", 275 | "output_type": "stream", 276 | "text": [ 277 | "Vocabulary Size: 3954\n" 278 | ] 279 | } 280 | ], 281 | "source": [ 282 | "vocabSize = wordVectorsDF.count()\n", 283 | "print \"Vocabulary Size: \", vocabSize " 284 | ] 285 | }, 286 | { 287 | "cell_type": "markdown", 288 | "metadata": {}, 289 | "source": [ 290 | "# Find top N closest words " 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": 11, 296 | "metadata": { 297 | "collapsed": false 298 | }, 299 | "outputs": [ 300 | { 301 | "data": { 302 | "text/html": [ 303 | "
\n", 304 | "\n", 305 | " \n", 306 | " \n", 307 | " \n", 308 | " \n", 309 | " \n", 310 | " \n", 311 | " \n", 312 | " \n", 313 | " \n", 314 | " \n", 315 | " \n", 316 | " \n", 317 | " \n", 318 | " \n", 319 | " \n", 320 | " \n", 321 | " \n", 322 | " \n", 323 | " \n", 324 | " \n", 325 | " \n", 326 | " \n", 327 | " \n", 328 | " \n", 329 | " \n", 330 | " \n", 331 | " \n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | " \n", 356 | " \n", 357 | " \n", 358 | " \n", 359 | " \n", 360 | " \n", 361 | " \n", 362 | " \n", 363 | " \n", 364 | " \n", 365 | " \n", 366 | " \n", 367 | " \n", 368 | " \n", 369 | " \n", 370 | " \n", 371 | " \n", 372 | " \n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " \n", 377 | " \n", 378 | " \n", 379 | "
wordsimilarity
0eve0.913197
1gift!0.809615
2eve!0.757804
3😍😍😍0.750454
4cup0.749197
5indirect0.734012
6christmas'0.729327
7merry0.728448
8tomorrow0.718660
9wishing0.703304
10life?0.702444
11🎄0.700686
12xmas0.697368
\n", 380 | "
" 381 | ], 382 | "text/plain": [ 383 | " word similarity\n", 384 | "0 eve 0.913197\n", 385 | "1 gift! 0.809615\n", 386 | "2 eve! 0.757804\n", 387 | "3 😍😍😍 0.750454\n", 388 | "4 cup 0.749197\n", 389 | "5 indirect 0.734012\n", 390 | "6 christmas' 0.729327\n", 391 | "7 merry 0.728448\n", 392 | "8 tomorrow 0.718660\n", 393 | "9 wishing 0.703304\n", 394 | "10 life? 0.702444\n", 395 | "11 🎄 0.700686\n", 396 | "12 xmas 0.697368" 397 | ] 398 | }, 399 | "execution_count": 11, 400 | "metadata": {}, 401 | "output_type": "execute_result" 402 | } 403 | ], 404 | "source": [ 405 | "topN = 13\n", 406 | "synonymsDF = modelW2V.findSynonyms('christmas', topN).toPandas()\n", 407 | "synonymsDF " 408 | ] 409 | }, 410 | { 411 | "cell_type": "markdown", 412 | "metadata": {}, 413 | "source": [ 414 | "# As Expected, Unrelated terms are Not Accurate" 415 | ] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "execution_count": 12, 420 | "metadata": { 421 | "collapsed": false 422 | }, 423 | "outputs": [ 424 | { 425 | "data": { 426 | "text/html": [ 427 | "
\n", 428 | "\n", 429 | " \n", 430 | " \n", 431 | " \n", 432 | " \n", 433 | " \n", 434 | " \n", 435 | " \n", 436 | " \n", 437 | " \n", 438 | " \n", 439 | " \n", 440 | " \n", 441 | " \n", 442 | " \n", 443 | " \n", 444 | " \n", 445 | " \n", 446 | " \n", 447 | " \n", 448 | " \n", 449 | " \n", 450 | " \n", 451 | " \n", 452 | " \n", 453 | " \n", 454 | " \n", 455 | " \n", 456 | " \n", 457 | " \n", 458 | " \n", 459 | " \n", 460 | " \n", 461 | " \n", 462 | " \n", 463 | "
wordsimilarity
0com0.367090
1bathroom0.366653
2talks0.365062
3too!0.364378
4points0.360151
\n", 464 | "
" 465 | ], 466 | "text/plain": [ 467 | " word similarity\n", 468 | "0 com 0.367090\n", 469 | "1 bathroom 0.366653\n", 470 | "2 talks 0.365062\n", 471 | "3 too! 0.364378\n", 472 | "4 points 0.360151" 473 | ] 474 | }, 475 | "execution_count": 12, 476 | "metadata": {}, 477 | "output_type": "execute_result" 478 | } 479 | ], 480 | "source": [ 481 | "synonymsDF = modelW2V.findSynonyms('dog', 5).toPandas()\n", 482 | "synonymsDF " 483 | ] 484 | }, 485 | { 486 | "cell_type": "code", 487 | "execution_count": null, 488 | "metadata": { 489 | "collapsed": true 490 | }, 491 | "outputs": [], 492 | "source": [] 493 | }, 494 | { 495 | "cell_type": "code", 496 | "execution_count": null, 497 | "metadata": { 498 | "collapsed": true 499 | }, 500 | "outputs": [], 501 | "source": [] 502 | }, 503 | { 504 | "cell_type": "markdown", 505 | "metadata": {}, 506 | "source": [ 507 | "# PCA on Top of Word2Vec using DF (spark.ml)" 508 | ] 509 | }, 510 | { 511 | "cell_type": "code", 512 | "execution_count": 13, 513 | "metadata": { 514 | "collapsed": false 515 | }, 516 | "outputs": [], 517 | "source": [ 518 | "dfW2V = wordVectorsDF.select('vector').withColumnRenamed('vector','features')\n", 519 | "\n", 520 | "numComponents = 3\n", 521 | "pca = PCA(k = numComponents, inputCol = 'features', outputCol = 'pcaFeatures')\n", 522 | "model = pca.fit(dfW2V)\n", 523 | "dfComp = model.transform(dfW2V).select(\"pcaFeatures\")" 524 | ] 525 | }, 526 | { 527 | "cell_type": "markdown", 528 | "metadata": {}, 529 | "source": [ 530 | "# 3D Visualization" 531 | ] 532 | }, 533 | { 534 | "cell_type": "code", 535 | "execution_count": 14, 536 | "metadata": { 537 | "collapsed": false 538 | }, 539 | "outputs": [ 540 | { 541 | "name": "stderr", 542 | "output_type": "stream", 543 | "text": [ 544 | "/Users/jorgecastanon/anaconda/lib/python2.7/site-packages/matplotlib/collections.py:590: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison\n", 545 | " if self._edgecolors == str('face'):\n" 546 | ] 547 | } 548 | ], 549 | "source": [ 550 | "word = 'christmas'\n", 551 | "nwords = 200\n", 552 | "\n", 553 | "#############\n", 554 | "\n", 555 | "r = wvu.topNwordsToPlot(dfComp,wordVectorsDF,word,nwords)\n", 556 | "\n", 557 | "############\n", 558 | "fs=20 #fontsize\n", 559 | "w = r['word']\n", 560 | "fig = plt.figure()\n", 561 | "ax = fig.add_subplot(111, projection='3d')\n", 562 | "\n", 563 | "height = 10\n", 564 | "width = 10\n", 565 | "fig.set_size_inches(width, height)\n", 566 | "\n", 567 | "ax.scatter(r['X'], r['Y'], r['Z'], color='red', s=100, marker='o', edgecolors='black')\n", 568 | "for i, txt in enumerate(w):\n", 569 | " if(i<7):\n", 570 | " ax.text(r['X'].ix[i],r['Y'].ix[i],r['Z'].ix[i], '%s' % (txt), size=20, zorder=1, color='k')\n", 571 | "ax.set_xlabel('1st. Component', fontsize=fs)\n", 572 | "ax.set_ylabel('2nd. Component', fontsize=fs)\n", 573 | "ax.set_zlabel('3rd. Component', fontsize=fs)\n", 574 | "ax.set_title('Visualization of Word2Vec via PCA', fontsize=fs)\n", 575 | "ax.grid(True)\n", 576 | "plt.show()" 577 | ] 578 | }, 579 | { 580 | "cell_type": "code", 581 | "execution_count": null, 582 | "metadata": { 583 | "collapsed": true 584 | }, 585 | "outputs": [], 586 | "source": [] 587 | }, 588 | { 589 | "cell_type": "code", 590 | "execution_count": null, 591 | "metadata": { 592 | "collapsed": true 593 | }, 594 | "outputs": [], 595 | "source": [] 596 | }, 597 | { 598 | "cell_type": "code", 599 | "execution_count": null, 600 | "metadata": { 601 | "collapsed": true 602 | }, 603 | "outputs": [], 604 | "source": [] 605 | }, 606 | { 607 | "cell_type": "code", 608 | "execution_count": null, 609 | "metadata": { 610 | "collapsed": true 611 | }, 612 | "outputs": [], 613 | "source": [] 614 | }, 615 | { 616 | "cell_type": "code", 617 | "execution_count": null, 618 | "metadata": { 619 | "collapsed": true 620 | }, 621 | "outputs": [], 622 | "source": [] 623 | }, 624 | { 625 | "cell_type": "code", 626 | "execution_count": null, 627 | "metadata": { 628 | "collapsed": true 629 | }, 630 | "outputs": [], 631 | "source": [] 632 | }, 633 | { 634 | "cell_type": "markdown", 635 | "metadata": {}, 636 | "source": [ 637 | "# K-means on top of Word2Vec using DF (spark.ml)" 638 | ] 639 | }, 640 | { 641 | "cell_type": "code", 642 | "execution_count": 20, 643 | "metadata": { 644 | "collapsed": false 645 | }, 646 | "outputs": [ 647 | { 648 | "name": "stdout", 649 | "output_type": "stream", 650 | "text": [ 651 | "Number of Clusters (K) Used: 44\n", 652 | "Elapsed time (seconds) : 1.38142800331\n" 653 | ] 654 | } 655 | ], 656 | "source": [ 657 | "t0=time.time()\n", 658 | "\n", 659 | "K = int(math.floor(math.sqrt(float(vocabSize)/2)))\n", 660 | " # K ~ sqrt(n/2) this is a rule of thumb for choosing K,\n", 661 | " # where n is the number of words in the model\n", 662 | " # feel free to choose K with a fancier algorithm\n", 663 | " \n", 664 | "dfW2V = wordVectorsDF.select('vector').withColumnRenamed('vector','features')\n", 665 | "kmeans = KMeans(k=K, seed=1)\n", 666 | "modelK = kmeans.fit(dfW2V)\n", 667 | "labelsDF = modelK.transform(dfW2V).select('prediction').withColumnRenamed('prediction','labels')\n", 668 | "\n", 669 | "print \"Number of Clusters (K) Used: \", K\n", 670 | "print \"Elapsed time (seconds) :\", time.time() - t0" 671 | ] 672 | }, 673 | { 674 | "cell_type": "code", 675 | "execution_count": null, 676 | "metadata": { 677 | "collapsed": true 678 | }, 679 | "outputs": [], 680 | "source": [] 681 | } 682 | ], 683 | "metadata": { 684 | "kernelspec": { 685 | "display_name": "Python 2", 686 | "language": "python", 687 | "name": "python2" 688 | }, 689 | "language_info": { 690 | "codemirror_mode": { 691 | "name": "ipython", 692 | "version": 2 693 | }, 694 | "file_extension": ".py", 695 | "mimetype": "text/x-python", 696 | "name": "python", 697 | "nbconvert_exporter": "python", 698 | "pygments_lexer": "ipython2", 699 | "version": "2.7.11" 700 | } 701 | }, 702 | "nbformat": 4, 703 | "nbformat_minor": 0 704 | } 705 | -------------------------------------------------------------------------------- /ml-scripts/.ipynb_checkpoints/Word2Vec with Tweets-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import pandas as pd\n", 13 | "import time\n", 14 | "import math\n", 15 | "from nltk.corpus import stopwords\n", 16 | "\n", 17 | "from pyspark import SparkContext\n", 18 | "from pyspark import Row\n", 19 | "from pyspark.sql import SQLContext\n", 20 | "from pyspark.ml.feature import Word2Vec\n", 21 | "from pyspark.ml.clustering import KMeans\n", 22 | "from pyspark.mllib.linalg import Vectors\n", 23 | "from pyspark.ml.feature import PCA\n", 24 | "\n", 25 | "import matplotlib.pyplot as plt\n", 26 | "from mpl_toolkits.mplot3d import Axes3D\n", 27 | "\n", 28 | "import word2vecUtilities as wvu" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "# Read Twitter Data as a Spark DataFrame" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 2, 41 | "metadata": { 42 | "collapsed": false 43 | }, 44 | "outputs": [ 45 | { 46 | "name": "stdout", 47 | "output_type": "stream", 48 | "text": [ 49 | "Number of tweets read: 239082\n", 50 | "Elapsed time (seconds): 33.6140749454\n" 51 | ] 52 | } 53 | ], 54 | "source": [ 55 | "t0 = time.time()\n", 56 | "datapath = '/Users/jorgecastanon/Documents/github/w2v/data/tweets.gz'\n", 57 | "tweets = sqlContext.read.json(datapath)\n", 58 | "tweets.registerTempTable(\"tweets\")\n", 59 | "twr = tweets.count()\n", 60 | "print \"Number of tweets read: \", twr \n", 61 | "# this line add ~7 seconds (from ~24.5 seconds to ~31.5 seconds)\n", 62 | "# Number of tweets read: 239082\n", 63 | "print \"Elapsed time (seconds): \", time.time() - t0\n", 64 | "#Elapsed time (seconds): 31.9646401405" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "# Read Keywords: christmas, santa, turkey, ..." 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 4, 77 | "metadata": { 78 | "collapsed": false 79 | }, 80 | "outputs": [ 81 | { 82 | "data": { 83 | "text/html": [ 84 | "
\n", 85 | "\n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | "
0
0santa
1claus
2merry
3christmas
4eve
\n", 115 | "
" 116 | ], 117 | "text/plain": [ 118 | " 0\n", 119 | "0 santa\n", 120 | "1 claus\n", 121 | "2 merry\n", 122 | "3 christmas\n", 123 | "4 eve" 124 | ] 125 | }, 126 | "execution_count": 4, 127 | "metadata": {}, 128 | "output_type": "execute_result" 129 | } 130 | ], 131 | "source": [ 132 | "filterPath = '/Users/jorgecastanon/Documents/github/w2v/data/filter.txt'\n", 133 | "filter = pd.read_csv(filterPath,header=None)\n", 134 | "filter.head()" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "# Use Spark SQL to Filter Tweets:\n", 142 | "### + In english\n", 143 | "### + And containing at least one keyword" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": 5, 149 | "metadata": { 150 | "collapsed": false 151 | }, 152 | "outputs": [ 153 | { 154 | "name": "stdout", 155 | "output_type": "stream", 156 | "text": [ 157 | "Number of tweets after filtering: 15999\n", 158 | "Elapsed time (seconds): 10.3279871941\n", 159 | "Percetage of Tweets Used: 0.0669184631214\n" 160 | ] 161 | } 162 | ], 163 | "source": [ 164 | "# Construct SQL Command\n", 165 | "t0 = time.time()\n", 166 | "sqlString = \"(\"\n", 167 | "for substr in filter[0]: #iteration on the list of words to filter (at most 50-100 words)\n", 168 | " sqlString = sqlString+\"text LIKE '%\"+substr+\"%' OR \"\n", 169 | " sqlString = sqlString+\"text LIKE '%\"+substr.upper()+\"%' OR \"\n", 170 | "sqlString=sqlString[:-4]+\")\"\n", 171 | "sqlFilterCommand = \"SELECT lang, text FROM tweets WHERE (lang = 'en') AND \"+sqlString\n", 172 | "\n", 173 | "# Query tweets in english that contain at least one of the keywords\n", 174 | "tweetsDF = sqlContext.sql(sqlFilterCommand).cache()\n", 175 | "twf = tweetsDF.count()\n", 176 | "print \"Number of tweets after filtering: \", twf \n", 177 | "# last line add ~9 seconds (from ~0.72 seconds to ~9.42 seconds)\n", 178 | "print \"Elapsed time (seconds): \", time.time() - t0\n", 179 | "\n", 180 | "print \"Percetage of Tweets Used: \", float(twf)/twr" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": {}, 186 | "source": [ 187 | "# Parse Tweets and Remove Stop Words " 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": 6, 193 | "metadata": { 194 | "collapsed": false 195 | }, 196 | "outputs": [], 197 | "source": [ 198 | "tweetsRDD = tweetsDF.select('text').rdd\n", 199 | "\n", 200 | "def parseAndRemoveStopWords(text):\n", 201 | " t = text[0].replace(\";\",\" \").replace(\":\",\" \").replace('\"',' ').replace('-',' ')\n", 202 | " t = t.replace(',',' ').replace('.',' ')\n", 203 | " t = t.lower().split(\" \")\n", 204 | " stop = stopwords.words('english')\n", 205 | " return [i for i in t if i not in stop]\n", 206 | "\n", 207 | "tw = tweetsRDD.map(parseAndRemoveStopWords)" 208 | ] 209 | }, 210 | { 211 | "cell_type": "markdown", 212 | "metadata": {}, 213 | "source": [ 214 | "# Word2Vec: returns a dataframe with words and vectors\n", 215 | "\n", 216 | "+ Sometimes you need to run this block twice (strange reason that need to de-bug)" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 8, 222 | "metadata": { 223 | "collapsed": false 224 | }, 225 | "outputs": [ 226 | { 227 | "name": "stdout", 228 | "output_type": "stream", 229 | "text": [ 230 | "Elapsed time (seconds) to train Word2Vec: 8.02205491066\n" 231 | ] 232 | } 233 | ], 234 | "source": [ 235 | "# map to df\n", 236 | "twDF = tw.map(lambda p: Row(text=p)).toDF()\n", 237 | "\n", 238 | "# default minCount = 5 (we may need to try something larger: 20-100 to reduce cost)\n", 239 | "# default vectorSize = 100 (we may want to keep default)\n", 240 | "t0 = time.time()\n", 241 | "word2Vec = Word2Vec(vectorSize=100, minCount=5, inputCol=\"text\", outputCol=\"result\")\n", 242 | "modelW2V = word2Vec.fit(twDF)\n", 243 | "wordVectorsDF = modelW2V.getVectors()\n", 244 | "print \"Elapsed time (seconds) to train Word2Vec: \", time.time() - t0" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 9, 250 | "metadata": { 251 | "collapsed": false 252 | }, 253 | "outputs": [ 254 | { 255 | "name": "stdout", 256 | "output_type": "stream", 257 | "text": [ 258 | "1.5.1\n" 259 | ] 260 | } 261 | ], 262 | "source": [ 263 | "print sc.version" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": 10, 269 | "metadata": { 270 | "collapsed": false 271 | }, 272 | "outputs": [ 273 | { 274 | "name": "stdout", 275 | "output_type": "stream", 276 | "text": [ 277 | "Vocabulary Size: 3954\n" 278 | ] 279 | } 280 | ], 281 | "source": [ 282 | "vocabSize = wordVectorsDF.count()\n", 283 | "print \"Vocabulary Size: \", vocabSize " 284 | ] 285 | }, 286 | { 287 | "cell_type": "markdown", 288 | "metadata": {}, 289 | "source": [ 290 | "# Find top N closest words " 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": 11, 296 | "metadata": { 297 | "collapsed": false 298 | }, 299 | "outputs": [ 300 | { 301 | "data": { 302 | "text/html": [ 303 | "
\n", 304 | "\n", 305 | " \n", 306 | " \n", 307 | " \n", 308 | " \n", 309 | " \n", 310 | " \n", 311 | " \n", 312 | " \n", 313 | " \n", 314 | " \n", 315 | " \n", 316 | " \n", 317 | " \n", 318 | " \n", 319 | " \n", 320 | " \n", 321 | " \n", 322 | " \n", 323 | " \n", 324 | " \n", 325 | " \n", 326 | " \n", 327 | " \n", 328 | " \n", 329 | " \n", 330 | " \n", 331 | " \n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | " \n", 356 | " \n", 357 | " \n", 358 | " \n", 359 | " \n", 360 | " \n", 361 | " \n", 362 | " \n", 363 | " \n", 364 | " \n", 365 | " \n", 366 | " \n", 367 | " \n", 368 | " \n", 369 | " \n", 370 | " \n", 371 | " \n", 372 | " \n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " \n", 377 | " \n", 378 | " \n", 379 | "
wordsimilarity
0eve0.913197
1gift!0.809615
2eve!0.757804
3😍😍😍0.750454
4cup0.749197
5indirect0.734012
6christmas'0.729327
7merry0.728448
8tomorrow0.718660
9wishing0.703304
10life?0.702444
11🎄0.700686
12xmas0.697368
\n", 380 | "
" 381 | ], 382 | "text/plain": [ 383 | " word similarity\n", 384 | "0 eve 0.913197\n", 385 | "1 gift! 0.809615\n", 386 | "2 eve! 0.757804\n", 387 | "3 😍😍😍 0.750454\n", 388 | "4 cup 0.749197\n", 389 | "5 indirect 0.734012\n", 390 | "6 christmas' 0.729327\n", 391 | "7 merry 0.728448\n", 392 | "8 tomorrow 0.718660\n", 393 | "9 wishing 0.703304\n", 394 | "10 life? 0.702444\n", 395 | "11 🎄 0.700686\n", 396 | "12 xmas 0.697368" 397 | ] 398 | }, 399 | "execution_count": 11, 400 | "metadata": {}, 401 | "output_type": "execute_result" 402 | } 403 | ], 404 | "source": [ 405 | "topN = 13\n", 406 | "synonymsDF = modelW2V.findSynonyms('christmas', topN).toPandas()\n", 407 | "synonymsDF " 408 | ] 409 | }, 410 | { 411 | "cell_type": "markdown", 412 | "metadata": {}, 413 | "source": [ 414 | "# As Expected, Unrelated terms are Not Accurate" 415 | ] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "execution_count": 12, 420 | "metadata": { 421 | "collapsed": false 422 | }, 423 | "outputs": [ 424 | { 425 | "data": { 426 | "text/html": [ 427 | "
\n", 428 | "\n", 429 | " \n", 430 | " \n", 431 | " \n", 432 | " \n", 433 | " \n", 434 | " \n", 435 | " \n", 436 | " \n", 437 | " \n", 438 | " \n", 439 | " \n", 440 | " \n", 441 | " \n", 442 | " \n", 443 | " \n", 444 | " \n", 445 | " \n", 446 | " \n", 447 | " \n", 448 | " \n", 449 | " \n", 450 | " \n", 451 | " \n", 452 | " \n", 453 | " \n", 454 | " \n", 455 | " \n", 456 | " \n", 457 | " \n", 458 | " \n", 459 | " \n", 460 | " \n", 461 | " \n", 462 | " \n", 463 | "
wordsimilarity
0com0.367090
1bathroom0.366653
2talks0.365062
3too!0.364378
4points0.360151
\n", 464 | "
" 465 | ], 466 | "text/plain": [ 467 | " word similarity\n", 468 | "0 com 0.367090\n", 469 | "1 bathroom 0.366653\n", 470 | "2 talks 0.365062\n", 471 | "3 too! 0.364378\n", 472 | "4 points 0.360151" 473 | ] 474 | }, 475 | "execution_count": 12, 476 | "metadata": {}, 477 | "output_type": "execute_result" 478 | } 479 | ], 480 | "source": [ 481 | "synonymsDF = modelW2V.findSynonyms('dog', 5).toPandas()\n", 482 | "synonymsDF " 483 | ] 484 | }, 485 | { 486 | "cell_type": "code", 487 | "execution_count": null, 488 | "metadata": { 489 | "collapsed": true 490 | }, 491 | "outputs": [], 492 | "source": [] 493 | }, 494 | { 495 | "cell_type": "code", 496 | "execution_count": null, 497 | "metadata": { 498 | "collapsed": true 499 | }, 500 | "outputs": [], 501 | "source": [] 502 | }, 503 | { 504 | "cell_type": "markdown", 505 | "metadata": {}, 506 | "source": [ 507 | "# PCA on Top of Word2Vec using DF (spark.ml)" 508 | ] 509 | }, 510 | { 511 | "cell_type": "code", 512 | "execution_count": 13, 513 | "metadata": { 514 | "collapsed": false 515 | }, 516 | "outputs": [], 517 | "source": [ 518 | "dfW2V = wordVectorsDF.select('vector').withColumnRenamed('vector','features')\n", 519 | "\n", 520 | "numComponents = 3\n", 521 | "pca = PCA(k = numComponents, inputCol = 'features', outputCol = 'pcaFeatures')\n", 522 | "model = pca.fit(dfW2V)\n", 523 | "dfComp = model.transform(dfW2V).select(\"pcaFeatures\")" 524 | ] 525 | }, 526 | { 527 | "cell_type": "markdown", 528 | "metadata": {}, 529 | "source": [ 530 | "# 3D Visualization" 531 | ] 532 | }, 533 | { 534 | "cell_type": "code", 535 | "execution_count": 14, 536 | "metadata": { 537 | "collapsed": false 538 | }, 539 | "outputs": [ 540 | { 541 | "name": "stderr", 542 | "output_type": "stream", 543 | "text": [ 544 | "/Users/jorgecastanon/anaconda/lib/python2.7/site-packages/matplotlib/collections.py:590: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison\n", 545 | " if self._edgecolors == str('face'):\n" 546 | ] 547 | } 548 | ], 549 | "source": [ 550 | "word = 'christmas'\n", 551 | "nwords = 200\n", 552 | "\n", 553 | "#############\n", 554 | "\n", 555 | "r = wvu.topNwordsToPlot(dfComp,wordVectorsDF,word,nwords)\n", 556 | "\n", 557 | "############\n", 558 | "fs=20 #fontsize\n", 559 | "w = r['word']\n", 560 | "fig = plt.figure()\n", 561 | "ax = fig.add_subplot(111, projection='3d')\n", 562 | "\n", 563 | "height = 10\n", 564 | "width = 10\n", 565 | "fig.set_size_inches(width, height)\n", 566 | "\n", 567 | "ax.scatter(r['X'], r['Y'], r['Z'], color='red', s=100, marker='o', edgecolors='black')\n", 568 | "for i, txt in enumerate(w):\n", 569 | " if(i<7):\n", 570 | " ax.text(r['X'].ix[i],r['Y'].ix[i],r['Z'].ix[i], '%s' % (txt), size=20, zorder=1, color='k')\n", 571 | "ax.set_xlabel('1st. Component', fontsize=fs)\n", 572 | "ax.set_ylabel('2nd. Component', fontsize=fs)\n", 573 | "ax.set_zlabel('3rd. Component', fontsize=fs)\n", 574 | "ax.set_title('Visualization of Word2Vec via PCA', fontsize=fs)\n", 575 | "ax.grid(True)\n", 576 | "plt.show()" 577 | ] 578 | }, 579 | { 580 | "cell_type": "code", 581 | "execution_count": null, 582 | "metadata": { 583 | "collapsed": true 584 | }, 585 | "outputs": [], 586 | "source": [] 587 | }, 588 | { 589 | "cell_type": "code", 590 | "execution_count": null, 591 | "metadata": { 592 | "collapsed": true 593 | }, 594 | "outputs": [], 595 | "source": [] 596 | }, 597 | { 598 | "cell_type": "code", 599 | "execution_count": null, 600 | "metadata": { 601 | "collapsed": true 602 | }, 603 | "outputs": [], 604 | "source": [] 605 | }, 606 | { 607 | "cell_type": "code", 608 | "execution_count": null, 609 | "metadata": { 610 | "collapsed": true 611 | }, 612 | "outputs": [], 613 | "source": [] 614 | }, 615 | { 616 | "cell_type": "code", 617 | "execution_count": null, 618 | "metadata": { 619 | "collapsed": true 620 | }, 621 | "outputs": [], 622 | "source": [] 623 | }, 624 | { 625 | "cell_type": "code", 626 | "execution_count": null, 627 | "metadata": { 628 | "collapsed": true 629 | }, 630 | "outputs": [], 631 | "source": [] 632 | }, 633 | { 634 | "cell_type": "markdown", 635 | "metadata": {}, 636 | "source": [ 637 | "# K-means on top of Word2Vec using DF (spark.ml)" 638 | ] 639 | }, 640 | { 641 | "cell_type": "code", 642 | "execution_count": 20, 643 | "metadata": { 644 | "collapsed": false 645 | }, 646 | "outputs": [ 647 | { 648 | "name": "stdout", 649 | "output_type": "stream", 650 | "text": [ 651 | "Number of Clusters (K) Used: 44\n", 652 | "Elapsed time (seconds) : 1.38142800331\n" 653 | ] 654 | } 655 | ], 656 | "source": [ 657 | "t0=time.time()\n", 658 | "\n", 659 | "K = int(math.floor(math.sqrt(float(vocabSize)/2)))\n", 660 | " # K ~ sqrt(n/2) this is a rule of thumb for choosing K,\n", 661 | " # where n is the number of words in the model\n", 662 | " # feel free to choose K with a fancier algorithm\n", 663 | " \n", 664 | "dfW2V = wordVectorsDF.select('vector').withColumnRenamed('vector','features')\n", 665 | "kmeans = KMeans(k=K, seed=1)\n", 666 | "modelK = kmeans.fit(dfW2V)\n", 667 | "labelsDF = modelK.transform(dfW2V).select('prediction').withColumnRenamed('prediction','labels')\n", 668 | "\n", 669 | "print \"Number of Clusters (K) Used: \", K\n", 670 | "print \"Elapsed time (seconds) :\", time.time() - t0" 671 | ] 672 | }, 673 | { 674 | "cell_type": "code", 675 | "execution_count": null, 676 | "metadata": { 677 | "collapsed": true 678 | }, 679 | "outputs": [], 680 | "source": [] 681 | } 682 | ], 683 | "metadata": { 684 | "kernelspec": { 685 | "display_name": "Python 2", 686 | "language": "python", 687 | "name": "python2" 688 | }, 689 | "language_info": { 690 | "codemirror_mode": { 691 | "name": "ipython", 692 | "version": 2 693 | }, 694 | "file_extension": ".py", 695 | "mimetype": "text/x-python", 696 | "name": "python", 697 | "nbconvert_exporter": "python", 698 | "pygments_lexer": "ipython2", 699 | "version": "2.7.11" 700 | } 701 | }, 702 | "nbformat": 4, 703 | "nbformat_minor": 0 704 | } 705 | -------------------------------------------------------------------------------- /mllib-scripts/Word2Vec with Twitter Data using Spark RDDs.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Building a Word2Vec Model with Twitter Data" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "The content was written by:\n", 15 | "[Jorge A Castañón](https://github.com/castanan/w2v)\n", 16 | "_The code used in this tutorial is adapted from Jorge A Castañón's GitHub page._" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "### Blog: https://ibm.biz/word2vec" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "
" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "# Step 1: Download Tweets" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": {}, 43 | "source": [ 44 | "Let's download the corpus of tweets, `tweets.gz` (118 Mb)" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "# Step 2: Download File with Keywords" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": {}, 57 | "source": [ 58 | "# Import Python and Pyspark Packages" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 1, 64 | "metadata": { 65 | "collapsed": true 66 | }, 67 | "outputs": [], 68 | "source": [ 69 | "import os, sys, codecs, json\n", 70 | "import numpy as np\n", 71 | "import pandas as pd\n", 72 | "import matplotlib.pyplot as plt\n", 73 | "from mpl_toolkits.mplot3d import Axes3D\n", 74 | "import math\n", 75 | "from IPython.display import display\n", 76 | "\n", 77 | "from pyspark.mllib.feature import Word2Vec\n", 78 | "from pyspark.mllib.clustering import KMeans\n", 79 | "from pyspark.mllib.linalg import Vectors\n", 80 | "from pyspark.ml.feature import PCA" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "# Step 3: Function to Filter Tweets and Train Word2Vec" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 3, 93 | "metadata": { 94 | "collapsed": false 95 | }, 96 | "outputs": [], 97 | "source": [ 98 | "def readFilters(filterpath):\n", 99 | " filters = set()\n", 100 | " f = open(filterpath, 'r')\n", 101 | " for line in f:\n", 102 | " line = codecs.decode(line, \"utf-8\")\n", 103 | " line = line.strip()\n", 104 | " if len(line) == 0: continue\n", 105 | " filters.add(line.lower())\n", 106 | " f.close()\n", 107 | " return filters\n", 108 | "\n", 109 | "def process(filters):\n", 110 | " def realProcess(line):\n", 111 | " key = 'text'\n", 112 | " try:\n", 113 | " t = json.loads(line)\n", 114 | " if key not in t or t['lang']!='en': return None\n", 115 | " value = t[key].lower()\n", 116 | " # match with filters\n", 117 | " found = False\n", 118 | " for ff in filters:\n", 119 | " if ff in value:\n", 120 | " return t\n", 121 | " break\n", 122 | " except Exception as e:\n", 123 | " return None\n", 124 | " return None\n", 125 | " return realProcess\n", 126 | "\n", 127 | "def filterAndTrainWord2vec(datapath,filterpath,word):\n", 128 | " \n", 129 | " filters = readFilters(filterpath) \n", 130 | " data = sc.textFile(datapath)\n", 131 | " totaltw = data.count()\n", 132 | "\n", 133 | " # the next line filters tweets of interest\n", 134 | " tweets = data.map(process(filters)).filter(lambda x: x != None).map(lambda t: t['text'])\n", 135 | " twcount = tweets.count()\n", 136 | " \n", 137 | " # the next line cleans unwanted characters and transform text to lower case\n", 138 | " tweets = tweets.map(lambda x: x.replace(\";\",\" \").replace(\":\",\" \").replace('\"',' ').replace('-',' ').replace(',',' ').replace('.',' ').lower())\n", 139 | " # the next line breaks tweets into words\n", 140 | " tweets = tweets.map(lambda row: row.split(\" \")) \n", 141 | " \n", 142 | " ## train Word2vec\n", 143 | " word2vec = Word2Vec()\n", 144 | " model = word2vec.fit(tweets) \n", 145 | "\n", 146 | " ## Get the list of words in the w2v matrix\n", 147 | " vocabsize = 10000\n", 148 | " any_word = word\n", 149 | " tmp_list = model.findSynonyms(any_word, vocabsize-1) #setting my vocabulary size to at most 100K words\n", 150 | " list_words = []\n", 151 | " for l in tmp_list:\n", 152 | " list_words.append(l[0])\n", 153 | " list_words.append(any_word)\n", 154 | "\n", 155 | " nwords = len(list_words)\n", 156 | " nfeatures = model.transform(any_word).array.shape[0]\n", 157 | " \n", 158 | " ## Construct the feature matrix, each row is asociated to each word in list_words\n", 159 | " feature_matrix = [] \n", 160 | " for word in list_words:\n", 161 | " feature_matrix.append(model.transform(word).array)\n", 162 | " \n", 163 | " ## save W2V matrix and the list of words \n", 164 | " np.save('/Users/jorgecastanon/Documents/github/w2v/mllib-scripts/myW2Vmatrix.npy',feature_matrix)\n", 165 | " np.save('/Users/jorgecastanon/Documents/github/w2v/mllib-scripts/myWordList.npy',list_words)\n", 166 | " \n", 167 | " print \"=================================================\"\n", 168 | " print \"Number of total tweets processed: \", totaltw\n", 169 | " print \"=================================================\"\n", 170 | " print \"Number of filtered tweets used: \", twcount\n", 171 | " print \"=================================================\"\n", 172 | " print \"Number of words in the model:\", nwords\n", 173 | " print \"=================================================\"\n", 174 | " print \"Number of features per word: \", nfeatures\n", 175 | " print \"=================================================\"\n", 176 | "\n", 177 | " return nwords" 178 | ] 179 | }, 180 | { 181 | "cell_type": "markdown", 182 | "metadata": {}, 183 | "source": [ 184 | "# Step 4: Cluster Words with K-means" 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": 4, 190 | "metadata": { 191 | "collapsed": false 192 | }, 193 | "outputs": [], 194 | "source": [ 195 | "def clusterWords(K):\n", 196 | " \n", 197 | " Feat = np.load('/Users/jorgecastanon/Documents/github/w2v/mllib-scripts/myW2Vmatrix.npy') # reads model generated by Word2Vec\n", 198 | " words = np.load('/Users/jorgecastanon/Documents/github/w2v/mllib-scripts/myWordList.npy') # reads list of words\n", 199 | " Featshape = Feat.shape\n", 200 | " \n", 201 | " Feat = sc.parallelize(Feat)\n", 202 | "\n", 203 | " ## K-means clustering with Spark \n", 204 | " maxiters=100\n", 205 | " clusters = KMeans.train(Feat, k = K, maxIterations = maxiters) \n", 206 | " \n", 207 | " ## Getting Cluster Labels for each Word and saving to a numpy file\n", 208 | " labels = Feat.map(lambda point: clusters.predict(point)) # add labels to each vector (word)\n", 209 | " list_labels = labels.collect()\n", 210 | " np.save('/Users/jorgecastanon/Documents/github/w2v/mllib-scripts/myClusters.npy',list_labels)\n", 211 | " \n", 212 | " print \"=\"*70\n", 213 | " print \"Size of the Word2vec matrix (words, features) is: \", Featshape \n", 214 | " print \"=\"*70\n", 215 | " print \"Number of clusters used: \", K\n", 216 | " print \"=\"*70\n" 217 | ] 218 | }, 219 | { 220 | "cell_type": "markdown", 221 | "metadata": {}, 222 | "source": [ 223 | "# Step 5: Function to Find Similar Words" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": 5, 229 | "metadata": { 230 | "collapsed": false 231 | }, 232 | "outputs": [], 233 | "source": [ 234 | "def findSimilarWords(word, nwords = 20):\n", 235 | " \n", 236 | " Feat = np.load('/Users/jorgecastanon/Documents/github/w2v/mllib-scripts/myW2Vmatrix.npy') \n", 237 | " words = np.load('/Users/jorgecastanon/Documents/github/w2v/mllib-scripts/myWordList.npy')\n", 238 | " labels = np.load('/Users/jorgecastanon/Documents/github/w2v/mllib-scripts/myClusters.npy')\n", 239 | " \n", 240 | " Nw = words.shape[0] # total number of words\n", 241 | " ind_star = np.where(word == words) # find index of the chosen word\n", 242 | " wstar = Feat[ind_star,:][0][0] # vector corresponding to the chosen 'word'\n", 243 | " nwstar = math.sqrt(np.dot(wstar,wstar)) # norm of vector corresponding to the chosen 'word'\n", 244 | "\n", 245 | " dist = np.zeros(Nw) # initialize vector of distances\n", 246 | " i = 0\n", 247 | " for w in Feat: # loop to compute cosine distances \n", 248 | " den = math.sqrt(np.dot(w,w))*nwstar # compute denominator of cosine distance\n", 249 | " dist[i] = abs( np.dot(wstar,w) ) / den # compute cosine distance to each word\n", 250 | " i = i + 1\n", 251 | "\n", 252 | " indexes = np.argpartition(dist,-(nwords+1))[-(nwords+1):]\n", 253 | " di = []\n", 254 | " for j in range(nwords+1):\n", 255 | " di.append(( words[indexes[j]], dist[indexes[j]], labels[indexes[j]] ) )\n", 256 | "\n", 257 | " result = pd.DataFrame(di, columns = [\"word\",\"similarity\",\"cluster\"])\n", 258 | " return result.iloc[::-1] # order results from closest to " 259 | ] 260 | }, 261 | { 262 | "cell_type": "markdown", 263 | "metadata": {}, 264 | "source": [ 265 | "# Step 6: Function to Visualize Words Using PCA" 266 | ] 267 | }, 268 | { 269 | "cell_type": "code", 270 | "execution_count": 6, 271 | "metadata": { 272 | "collapsed": false 273 | }, 274 | "outputs": [], 275 | "source": [ 276 | "def visualizeWords(maxWordsVis):\n", 277 | "\n", 278 | " Feat = np.load('/Users/jorgecastanon/Documents/github/w2v/mllib-scripts/myW2Vmatrix.npy') \n", 279 | " words = np.load('/Users/jorgecastanon/Documents/github/w2v/mllib-scripts/myWordList.npy')\n", 280 | " # to rdd, avoid this with big matrices by reading them directly from hdfs\n", 281 | " Feat = sc.parallelize(Feat) \n", 282 | " Feat = Feat.map(lambda vec: (Vectors.dense(vec),))\n", 283 | " # to dataframe\n", 284 | " dfFeat = sqlContext.createDataFrame(Feat,[\"features\"])\n", 285 | " \n", 286 | " ## PCA to project Feature matrix to 2 dimensions\n", 287 | " numComponents = 3\n", 288 | " pca = PCA(k = numComponents, inputCol = \"features\", outputCol = \"pcaFeatures\")\n", 289 | " model = pca.fit(dfFeat)\n", 290 | " dfComp = model.transform(dfFeat).select(\"pcaFeatures\")\n", 291 | " # get the first two components to lists to be plotted\n", 292 | " compX = dfComp.map(lambda vec: vec[0][0]).take(maxWordsVis)\n", 293 | " compY = dfComp.map(lambda vec: vec[0][1]).take(maxWordsVis)\n", 294 | " compZ = dfComp.map(lambda vec: vec[0][2]).take(maxWordsVis)\n", 295 | " return compX, compY, compZ, words" 296 | ] 297 | }, 298 | { 299 | "cell_type": "markdown", 300 | "metadata": {}, 301 | "source": [ 302 | "# Step 7: Compute and Show Results" 303 | ] 304 | }, 305 | { 306 | "cell_type": "markdown", 307 | "metadata": {}, 308 | "source": [ 309 | "### Filter Tweets and Train Word2Vec" 310 | ] 311 | }, 312 | { 313 | "cell_type": "code", 314 | "execution_count": 7, 315 | "metadata": { 316 | "collapsed": false 317 | }, 318 | "outputs": [ 319 | { 320 | "name": "stdout", 321 | "output_type": "stream", 322 | "text": [ 323 | "=================================================\n", 324 | "Number of total tweets processed: 239082\n", 325 | "=================================================\n", 326 | "Number of filtered tweets used: 23481\n", 327 | "=================================================\n", 328 | "Number of words in the model: 5648\n", 329 | "=================================================\n", 330 | "Number of features per word: 100\n", 331 | "=================================================\n" 332 | ] 333 | } 334 | ], 335 | "source": [ 336 | "pathToKeywords = '/Users/jorgecastanon/Documents/github/w2v/data/filter.txt'\n", 337 | "pathToTweets = '/Users/jorgecastanon/Documents/github/w2v/data/tweets.gz'\n", 338 | "word = 'christmas'\n", 339 | "numWords = filterAndTrainWord2vec(pathToTweets,pathToKeywords,word)" 340 | ] 341 | }, 342 | { 343 | "cell_type": "markdown", 344 | "metadata": {}, 345 | "source": [ 346 | "### Cluster Words with K-means" 347 | ] 348 | }, 349 | { 350 | "cell_type": "code", 351 | "execution_count": 8, 352 | "metadata": { 353 | "collapsed": false, 354 | "scrolled": true 355 | }, 356 | "outputs": [ 357 | { 358 | "name": "stdout", 359 | "output_type": "stream", 360 | "text": [ 361 | "======================================================================\n", 362 | "Size of the Word2vec matrix (words, features) is: (5648, 100)\n", 363 | "======================================================================\n", 364 | "Number of clusters used: 53\n", 365 | "======================================================================\n" 366 | ] 367 | } 368 | ], 369 | "source": [ 370 | "K = int(math.floor(math.sqrt(float(numWords)/2)))\n", 371 | " # K ~ sqrt(n/2) this is a rule of thumb for choosing K,\n", 372 | " # where n is the number of words in the model\n", 373 | " # feel free to choose K with a fancier algorithm\n", 374 | "\n", 375 | "clusterWords(K)" 376 | ] 377 | }, 378 | { 379 | "cell_type": "markdown", 380 | "metadata": {}, 381 | "source": [ 382 | "### Find Similar Words and Clusters" 383 | ] 384 | }, 385 | { 386 | "cell_type": "code", 387 | "execution_count": 9, 388 | "metadata": { 389 | "collapsed": false 390 | }, 391 | "outputs": [ 392 | { 393 | "data": { 394 | "text/html": [ 395 | "
\n", 396 | "\n", 397 | " \n", 398 | " \n", 399 | " \n", 400 | " \n", 401 | " \n", 402 | " \n", 403 | " \n", 404 | " \n", 405 | " \n", 406 | " \n", 407 | " \n", 408 | " \n", 409 | " \n", 410 | " \n", 411 | " \n", 412 | " \n", 413 | " \n", 414 | " \n", 415 | " \n", 416 | " \n", 417 | " \n", 418 | " \n", 419 | " \n", 420 | " \n", 421 | " \n", 422 | " \n", 423 | " \n", 424 | " \n", 425 | " \n", 426 | " \n", 427 | " \n", 428 | " \n", 429 | " \n", 430 | " \n", 431 | " \n", 432 | " \n", 433 | " \n", 434 | " \n", 435 | " \n", 436 | " \n", 437 | " \n", 438 | " \n", 439 | " \n", 440 | " \n", 441 | " \n", 442 | " \n", 443 | " \n", 444 | " \n", 445 | " \n", 446 | " \n", 447 | " \n", 448 | " \n", 449 | " \n", 450 | " \n", 451 | " \n", 452 | " \n", 453 | " \n", 454 | " \n", 455 | " \n", 456 | " \n", 457 | " \n", 458 | " \n", 459 | " \n", 460 | " \n", 461 | " \n", 462 | " \n", 463 | " \n", 464 | " \n", 465 | " \n", 466 | " \n", 467 | " \n", 468 | " \n", 469 | " \n", 470 | " \n", 471 | " \n", 472 | " \n", 473 | "
wordsimilaritycluster
10christmas1.00000026
9xmas0.63101226
8christmas'0.62446814
7christmas!0.57598426
6eve0.55838035
5eve!!0.48460717
4eve!!!!!0.47981117
3🎅🎄🎁0.47387817
2tomorrow0.47358432
1😍😍😍0.46950017
0very0.46919626
\n", 474 | "
" 475 | ], 476 | "text/plain": [ 477 | " word similarity cluster\n", 478 | "10 christmas 1.000000 26\n", 479 | "9 xmas 0.631012 26\n", 480 | "8 christmas' 0.624468 14\n", 481 | "7 christmas! 0.575984 26\n", 482 | "6 eve 0.558380 35\n", 483 | "5 eve!! 0.484607 17\n", 484 | "4 eve!!!!! 0.479811 17\n", 485 | "3 🎅🎄🎁 0.473878 17\n", 486 | "2 tomorrow 0.473584 32\n", 487 | "1 😍😍😍 0.469500 17\n", 488 | "0 very 0.469196 26" 489 | ] 490 | }, 491 | "metadata": {}, 492 | "output_type": "display_data" 493 | } 494 | ], 495 | "source": [ 496 | "res = findSimilarWords(word = 'christmas', nwords = 10)\n", 497 | "display(res)" 498 | ] 499 | }, 500 | { 501 | "cell_type": "markdown", 502 | "metadata": {}, 503 | "source": [ 504 | "### Use PCA to Visualize Words" 505 | ] 506 | }, 507 | { 508 | "cell_type": "code", 509 | "execution_count": 10, 510 | "metadata": { 511 | "collapsed": false 512 | }, 513 | "outputs": [], 514 | "source": [ 515 | "maxWordsVis = 15\n", 516 | "compX, compY, compZ, words = visualizeWords(maxWordsVis)" 517 | ] 518 | }, 519 | { 520 | "cell_type": "code", 521 | "execution_count": 11, 522 | "metadata": { 523 | "collapsed": false 524 | }, 525 | "outputs": [ 526 | { 527 | "name": "stderr", 528 | "output_type": "stream", 529 | "text": [ 530 | "/Users/jorgecastanon/anaconda/lib/python2.7/site-packages/matplotlib/collections.py:590: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison\n", 531 | " if self._edgecolors == str('face'):\n" 532 | ] 533 | }, 534 | { 535 | "data": { 536 | "image/png": "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\n9O/09QwfoMAAAAAASUVORK5CYII=\n", 537 | "text/plain": [ 538 | "" 539 | ] 540 | }, 541 | "metadata": {}, 542 | "output_type": "display_data" 543 | } 544 | ], 545 | "source": [ 546 | "%matplotlib inline\n", 547 | "fs=20 #fontsize\n", 548 | "w = words[0:maxWordsVis]\n", 549 | "fig = plt.figure()\n", 550 | "ax = fig.add_subplot(111, projection='3d')\n", 551 | "\n", 552 | "height = 10\n", 553 | "width = 10\n", 554 | "fig.set_size_inches(width, height)\n", 555 | "\n", 556 | "ax.scatter(compX, compY, compZ, color='red', s=100, marker='o', edgecolors='black')\n", 557 | "for i, txt in enumerate(w):\n", 558 | " ax.text(compX[i],compY[i],compZ[i], '%s' % (txt), size=8, zorder=1, color='k')\n", 559 | "ax.set_xlabel('1st. Component', fontsize=fs)\n", 560 | "ax.set_ylabel('2nd. Component', fontsize=fs)\n", 561 | "ax.set_zlabel('3rd. Component', fontsize=fs)\n", 562 | "ax.set_title('Visualization of Word2Vec via PCA', fontsize=fs)\n", 563 | "ax.grid(True)\n", 564 | "plt.show()" 565 | ] 566 | }, 567 | { 568 | "cell_type": "code", 569 | "execution_count": null, 570 | "metadata": { 571 | "collapsed": true 572 | }, 573 | "outputs": [], 574 | "source": [] 575 | }, 576 | { 577 | "cell_type": "code", 578 | "execution_count": null, 579 | "metadata": { 580 | "collapsed": true 581 | }, 582 | "outputs": [], 583 | "source": [] 584 | } 585 | ], 586 | "metadata": { 587 | "kernelspec": { 588 | "display_name": "Python 2", 589 | "language": "python", 590 | "name": "python2" 591 | }, 592 | "language_info": { 593 | "codemirror_mode": { 594 | "name": "ipython", 595 | "version": 2 596 | }, 597 | "file_extension": ".py", 598 | "mimetype": "text/x-python", 599 | "name": "python", 600 | "nbconvert_exporter": "python", 601 | "pygments_lexer": "ipython2", 602 | "version": "2.7.11" 603 | } 604 | }, 605 | "nbformat": 4, 606 | "nbformat_minor": 0 607 | } 608 | --------------------------------------------------------------------------------