利用Python機器學習框架scikit-learn,我們自己做一個分類模型,對中文評論信息做情感分析。其中還會介紹中文停用詞的處理方法。
疑惑
前些日子,我在微信后臺收到了一則讀者的留言。
我一下子有些懵——這怎么還帶點播了呢?
但是旋即我醒悟過來,好像是我自己之前挖了個坑。
之前我寫過《 如何用Python從海量文本抽取主題? 》一文,其中有這么一段:
為了演示的流暢,我們這里忽略了許多細節。很多內容使用的是預置默認參數,而且完全忽略了中文停用詞設置環節,因此“這個”、“如果”、“可能”、“就是”這樣的停用詞才會大搖大擺地出現在結果中。不過沒有關系,完成比完美重要得多。知道了問題所在,后面改進起來很容易。有機會我會寫文章介紹如何加入中文停用詞的去除環節。
根據“自己挖坑自己填”的法則,我決定把這一部分寫出來。
我可以使用偷懶的辦法。
例如在原先的教程里,更新中文停用詞處理部分,打個補丁。
但是,最近我發現,好像至今為止,我們的教程從來沒有介紹過如何用機器學習做情感分析。
你可能說,不對吧?
情感分析不是講過了嗎?老師你好像講過《 如何用Python做情感分析? 》,《 如何用Python做輿情時間序列可視化? 》和《 如何用Python和R對《權力的游戲》故事情節做情緒分析? 》。
你記得真清楚,提出表揚。
但是請注意,之前這幾篇文章中,并沒有使用機器學習方法。我們只不過調用了第三方提供的文本情感分析工具而已。
但是問題來了,這些第三方工具是在別的數據集上面訓練出來的,未必適合你的應用場景。
例如有些情感分析工具更適合分析新聞,有的更善于處理微博數據……你拿過來,卻是要對店鋪評論信息做分析。
這就如同你自己筆記本電腦里的網頁瀏覽器,和圖書館電子閱覽室的網頁瀏覽器,可能類型、版本完全一樣。但是你用起自己的瀏覽器,就是比公用電腦上的舒服、高效——因為你已經根據偏好,對自己瀏覽器上的“書簽”、“密碼存儲”、“稍后閱讀”都做了個性化設置。
咱們這篇文章,就給你講講如何利用Python和機器學習,自己訓練模型,對中文評論數據做情感分類。
# 數據
我的一個學生,利用爬蟲抓取了大眾點評網站上的數萬條餐廳評論數據。
這些數據在爬取時,包含了豐富的元數據類型。
我從中抽取了評論文本和評星(1-5星),用于本文的演示。
從這些數據里,我們隨機篩選評星為1,2,4,5的,各500條評論數據。一共2000條。
為什么只甩下評星數量為3的沒有選擇?
你先思考10秒鐘,然后往下看,核對答案。
答案是這樣的:
因為我們只希望對情感做出(正和負)二元分類,4和5星可以看作正向情感,1和2是負向情感……3怎么算?
所以,為了避免這種邊界不清晰造成的混淆,咱們只好把標為3星的內容丟棄掉了。
整理好之后的評論數據,如下圖所示。
我已經把數據放到了演示文件夾壓縮包里面。后文會給你提供下載路徑。
模型
使用機器學習的時候,你會遇到模型的選擇問題。
例如,許多模型都可以用來處理分類問題。邏輯回歸、決策樹、SVM、樸素貝葉斯……具體到咱們的評論信息情感分類問題,該用哪一種呢?
幸好,Python上的機器學習工具包 scikit-learn 不僅給我們提供了方便的接口,供我們調用,而且還非常貼心地幫我們做了小抄(cheat-sheet)。
這張圖看似密密麻麻,非常混亂,實際上是一個非常好的迷宮指南。其中綠色的方框,是各種機器學習模型。而藍色的圓圈,是你做判斷的地方。
你看,咱們要處理類別問題,對吧?
順著往下看,會要求你判斷數據是否有標記。我們有啊。
繼續往下走,數據小于100K嗎?
考慮一下,我們的數據有2000條,小于這個閾值。
接下來問是不是文本數據?是啊。
于是路徑到了終點。
Scikit-learn告訴我們:用樸素貝葉斯模型好了。
小抄都做得如此照顧用戶需求,你對scikit-learn的品質應該有個預期了吧?如果你需要使用經典機器學習模型(你可以理解成深度學習之外的所有模型),我推薦你先嘗試scikit-learn 。
向量化
《 如何用Python從海量文本抽取主題? 》一文里,我們講過自然語言處理時的向量化。
忘了?
沒關系。
子曰:
學而時習之,不亦樂乎?
這里咱們復習一下。
對自然語言文本做向量化(vectorization)的主要原因,是計算機看不懂自然語言。
計算機,顧名思義,就是用來算數的。文本對于它(至少到今天)沒有真正的意義。
但是自然語言的處理,是一個重要問題,也需要自動化的支持。因此人就得想辦法,讓機器能盡量理解和表示人類的語言。
假如這里有兩句話:
I love the game.
I hate the game.
那么我們就可以簡單粗暴地抽取出以下特征(其實就是把所有的單詞都羅列一遍):
- I
- love
- hate
- the
- game
對每一句話,都分別計算特征出現個數。于是上面兩句話就轉換為以下表格:
按照句子為單位,從左到右讀數字,第一句表示為[1, 1, 0, 1, 1],第二句就成了[1, 0, 1, 1, 1]。
這就叫向量化。
這個例子里面,特征的數量叫做維度。于是向量化之后的這兩句話,都有5個維度。
你一定要記住,此時機器依然不能理解兩句話的具體含義。但是它已經盡量在用一種有意義的方式來表達它們。
注意這里我們使用的,叫做“一袋子詞”(bag of words)模型。
下面這張圖(來自 https://goo.gl/2jJ9Kp ),形象化表示出這個模型的含義。
一袋子詞模型不考慮詞語的出現順序,也不考慮詞語和前后詞語之間的連接。每個詞都被當作一個獨立的特征來看待。
你可能會問:“這樣不是很不精確嗎?充分考慮順序和上下文聯系,不是更好嗎?”
沒錯,你對文本的順序、結構考慮得越周全,模型可以獲得的信息就越多。
但是,凡事都有成本。只需要用基礎的排列組合知識,你就能計算出獨立考慮單詞,和考慮連續n個詞語(稱作 n-gram),造成的模型維度差異了。
為了簡單起見,咱們這里還是先用一袋子詞吧。有空我再給你講講……
打住,不能再挖坑了。
中文
上一節咱們介紹的,是自然語言向量化處理的通則。
處理中文的時候,要更加麻煩一些。
因為不同于英文、法文等拉丁語系文字,中文天然沒有空格作為詞語之間的分割符號。
我們要先將中文分割成空格連接的詞語。
例如把:
“我喜歡這個游戲”
變成:
“我 喜歡 這個 游戲”
這樣一來,就可以仿照英文句子的向量化,來做中文的向量化了。
你可能擔心計算機處理起中文的詞語,跟處理英文詞語有所不同。
這種擔心沒必要。
因為咱們前面講過,計算機其實連英文單詞也看不懂。
在它眼里,不論什么自然語言的詞匯,都只是某種特定組合的字符串而已。
不論處理中文還是英文,都需要處理的一種詞匯,叫做停用詞。
中文維基百科里,是這么定義停用詞的:
在信息檢索中,為節省存儲空間和提高搜索效率,在處理自然語言數據(或文本)之前或之后會自動過濾掉某些字或詞,這些字或詞即被稱為Stop Words(停用詞)。
咱們做的,不是信息檢索,而已文本分類。
對咱們來說,你不打算拿它做特征的單詞,就可以當作停用詞。
還是舉剛才英文的例子,下面兩句話:
I love the game.
I hate the game.
告訴我,哪些是停用詞?
直覺會告訴你,定冠詞 the 應該是。
沒錯,它是虛詞,沒有什么特殊意義。
它在哪兒出現,都是一個意思。
一段文字里,出現很多次定冠詞都很正常。把它和那些包含信息更豐富的詞匯(例如love, hate)放在一起統計,就容易干擾我們把握文本的特征。
所以,咱們把它當作停用詞,從特征里面剔除出去。
舉一反三,你會發現分詞后的中文語句:
“我 喜歡 這個 游戲”
其中的“這個”應該也是停用詞吧?
答對了!
要處理停用詞,怎么辦呢?當然你可以一個個手工來尋找,但是那顯然效率太低。
有的機構或者團隊處理過許多停用詞。他們會發現,某種語言里,停用詞是有規律的。
他們把常見的停用詞總結出來,匯集成表格。以后只需要查表格,做處理,就可以利用先前的經驗和知識,提升效率,節約時間。
在scikit-learn中,英語停用詞是自帶的。只需要指定語言為英文,機器會幫助你自動處理它們。
但是中文……
scikit-learn開發團隊里,大概缺少足夠多的中文使用者吧。
好消息是,你可以使用第三方共享的停用詞表。
這種停用詞表到哪里下載呢?
我已經幫你找到了 一個 github 項目 ,里面包含了4種停用詞表,來自哈工大、四川大學和百度等自然語言處理方面的權威單位。
這幾個停用詞表文件長度不同,內容也差異很大。為了演示的方便與一致性,咱們統一先用哈工大這個停用詞表吧。
我已經將其一并存儲到了演示目錄壓縮包中,供你下載。
# 環境
請你先到 這個網址 下載本教程配套的壓縮包。
下載后解壓,你會在生成的目錄里面看到以下4個文件。
下文中,我們會把這個目錄稱為“演示目錄”。
請一定注意記好它的位置哦。
要裝Python,最簡便辦法是安裝Anaconda套裝。
請到 這個網址 下載Anaconda的最新版本。
請選擇左側的 Python 3.6 版本下載安裝。
如果你需要具體的步驟指導,或者想知道Windows平臺如何安裝并運行Anaconda命令,請參考我為你準備的 視頻教程 。
打開終端,用cd命令進入演示目錄。如果你不了解具體使用方法,也可以參考 視頻教程 。
我們需要使用許多軟件包。如果每一個都手動安裝,會非常麻煩。
我幫你做了個虛擬環境的配置文件,叫做environment.yaml ,也放在演示目錄中。
請你首先執行以下命令:
conda env create -f environment.yaml
這樣,所需的軟件包就一次性安裝完畢了。
之后執行,
source activate datapy3
進入這個虛擬環境。
注意一定要執行下面這句:
python -m ipykernel install --user --name=datapy3
只有這樣,當前的Python環境才會作為核心(kernel)在系統中注冊。
確認你的電腦上已經安裝了 Google Chrome 瀏覽器。如果沒有安裝請到這里 下載 安裝。
之后,在演示目錄中,我們執行:
jupyter notebook
Google Chrome會開啟,并啟動 Jupyter 筆記本界面:
你可以直接點擊文件列表中的demo.ipynb文件,可以看到本教程的全部示例代碼。
你可以一邊看教程的講解,一邊依次執行這些代碼。
但是,我建議的方法,是回到主界面下,新建一個新的空白 Python 3 (顯示名稱為datapy3的那個)筆記本。
請跟著教程,一個個字符輸入相應的內容。這可以幫助你更為深刻地理解代碼的含義,更高效地把技能內化。
準備工作結束,下面我們開始正式輸入代碼。
代碼
我們讀入數據框處理工具pandas。
import pandas as pd
利用pandas的csv讀取功能,把數據讀入。
注意為了與Excel和系統環境設置的兼容性,該csv數據文件采用的編碼為GB18030。這里需要顯式指定,否則會報錯。
df = pd.read_csv('data.csv', encoding='gb18030')
我們看看讀入是否正確。
df.head()
前5行內容如下:
看看數據框整體的形狀是怎么樣的:
df.shape
(2000, 2)
我們的數據一共2000行,2列。完整讀入。
我們并不準備把情感分析的結果分成4個類別。我們只打算分成正向和負向。
這里我們用一個無名函數來把評星數量>3的,當成正向情感,取值為1;反之視作負向情感,取值為0。
def make_label(df):
df["sentiment"] = df["star"].apply(lambda x: 1 if x>3 else 0)
編制好函數之后,我們實際運行在數據框上面。
make_label(df)
看看結果:
df.head()
從前5行看來,情感取值就是根據我們設定的規則,從評星數量轉化而來。
下面我們把特征和標簽拆開。
X = df[['comment']]
y = df.sentiment
X 是我們的全部特征。因為我們只用文本判斷情感,所以X實際上只有1列。
X.shape
(2000, 1)
而y是對應的標記數據。它也是只有1列。
y.shape
(2000,)
我們來看看 X 的前幾行數據。
X.head()
注意這里評論數據還是原始信息。詞語沒有進行拆分。
為了做特征向量化,下面我們利用結巴分詞工具來拆分句子為詞語。
import jieba
我們建立一個輔助函數,把結巴分詞的結果用空格連接。
這樣分詞后的結果就如同一個英文句子一樣,單次之間依靠空格分割。
def chinese_word_cut(mytext):
return " ".join(jieba.cut(mytext))
有了這個函數,我們就可以使用 apply 命令,把每一行的評論數據都進行分詞。
X['cutted_comment'] = X.comment.apply(chinese_word_cut)
我們看看分詞后的效果:
X.cutted_comment[:5]
單詞和標點之間都用空格分割,符合我們的要求。
下面就是機器學習的常規步驟了:我們需要把數據分成訓練集和測試集。
為什么要拆分數據集合?
在《貸還是不貸:如何用Python和機器學習幫你決策?》一文中,我已解釋過,這里復習一下:
如果期末考試之前,老師給你一套試題和答案,你把它背了下來。然后考試的時候,只是從那套試題里面抽取一部分考。你憑借超人的記憶力獲得了100分。請問你學會了這門課的知識了嗎?不知道如果給你新的題目,你會不會做呢?答案還是不知道。所以考試題目需要和復習題目有區別。
同樣的道理,假設咱們的模型只在某個數據集上訓練,準確度非常高,但是從來沒有見過其他新數據,那么它面對新數據表現如何呢?
你心里也沒底吧?
所以我們需要把數據集拆開,只在訓練集上訓練。保留測試集先不用,作為考試題,看模型經過訓練后的分類效果。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)
這里,我們設定了 random_state
取值,這是為了在不同環境中,保證隨機數取值一致,以便驗證咱們模型的實際效果。
我們看看此時的 X_train
數據集形狀。
X_train.shape
(1500, 2)
可見,在默認模式下,train_test_split
函數對訓練集和測試集的劃分比例為 3:1。
我們檢驗一下其他3個集合看看:
y_train.shape
(1500,)
X_test.shape
(500, 2)
y_test.shape
(500,)
同樣都正確無誤。
下面我們就要處理中文停用詞了。
我們編寫一個函數,從中文停用詞表里面,把停用詞作為列表格式保存并返回:
def get_custom_stopwords(stop_words_file):
with open(stop_words_file) as f:
stopwords = f.read()
stopwords_list = stopwords.split('\n')
custom_stopwords_list = [i for i in stopwords_list]
return custom_stopwords_list
我們指定使用的停用詞表,為我們已經下載保存好的哈工大停用詞表文件。
stop_words_file = "stopwordsHIT.txt"
stopwords = get_custom_stopwords(stop_words_file)
看看我們的停用詞列表的后10項:
stopwords[-10:]
這些大部分都是語氣助詞,作為停用詞去除掉,不會影響到語句的實質含義。
下面我們就要嘗試對分詞后的中文語句做向量化了。
我們讀入CountVectorizer
向量化工具,它依據詞語出現頻率轉化向量。
from sklearn.feature_extraction.text import CountVectorizer
我們建立一個CountVectorizer()
的實例,起名叫做vect。
注意這里為了說明停用詞的作用。我們先使用默認參數建立vect。
vect = CountVectorizer()
然后我們用向量化工具轉換已經分詞的訓練集語句,并且將其轉化為一個數據框,起名為term_matrix
。
term_matrix = pd.DataFrame(vect.fit_transform(X_train.cutted_comment).toarray(), columns=vect.get_feature_names())
我們看看term_matrix
的前5行:
term_matrix.head()
我們注意到,特征詞語五花八門,特別是很多數字都被當作特征放在了這里。
term_matrix
的形狀如下:
term_matrix.shape
(1500, 7305)
行數沒錯,列數就是特征個數,有7305個。
下面我們測試一下,加上停用詞去除功能,特征向量的轉化結果會有什么變化。
vect = CountVectorizer(stop_words=frozenset(stopwords))
下面的語句跟剛才一樣:
term_matrix = pd.DataFrame(vect.fit_transform(X_train.cutted_comment).toarray(), columns=vect.get_feature_names())
term_matrix.head()
可以看到,此時特征個數從剛才的7305個,降低為7144個。我們沒有調整任何其他的參數,因此減少的161個特征,就是出現在停用詞表中的單詞。
但是,這種停用詞表的寫法,依然會漏掉不少漏網之魚。
首先就是前面那一堆顯眼的數字。它們在此處作為特征毫無道理。如果沒有單位,沒有上下文,數字都是沒有意義的。
因此我們需要設定,數字不能作為特征。
在Python里面,我們可以設定token_pattern
來完成這個目標。
這一部分需要用到正則表達式的知識,我們這里無法詳細展開了。
但如果你只是需要去掉數字作為特征的話,按照我這樣寫,就可以了。
另一個問題在于,我們看到這個矩陣,實際上是個非常稀疏的矩陣,其中大部分的取值都是0.
這沒有關系,也很正常。
畢竟大部分評論語句當中只有幾個到幾十個詞語而已。7000多的特征,單個語句顯然是覆蓋不過來的。
然而,有些詞匯作為特征,就值得注意了。
首先是那些過于普遍的詞匯。盡管我們用了停用詞表,但是難免有些詞匯幾乎出現在每一句評論里。什么叫做特征?特征就是可以把一個事物與其他事物區別開的屬性。
假設讓你描述今天見到的印象最深刻的人。你怎么描述?
我看見他穿著小丑的衣服,在繁華的商業街踩高蹺,一邊走還一邊拋球,和路人打招呼。
還是……
我看見他有兩只眼睛,一只鼻子。
后者絕對不算是好的特征描述,因為難以把你要描述的個體區分出來。
物極必反,那些過于特殊的詞匯,其實也不應該保留。因為你了解了這個特征之后,對你的模型處理新的語句情感判斷,幾乎都用不上。
這就如同你跟著神仙學了屠龍之術,然而之后一輩子也沒有見過龍……
所以,如下面兩個代碼段所示,我們一共多設置了3層特征詞匯過濾。
max_df = 0.8 # 在超過這一比例的文檔中出現的關鍵詞(過于平凡),去除掉。
min_df = 3 # 在低于這一數量的文檔中出現的關鍵詞(過于獨特),去除掉。
vect = CountVectorizer(max_df = max_df,
min_df = min_df,
token_pattern=u'(?u)\\b[^\\d\\W]\\w+\\b',
stop_words=frozenset(stopwords))
這時候,再運行我們之前的語句,看看效果。
term_matrix = pd.DataFrame(vect.fit_transform(X_train.cutted_comment).toarray(), columns=vect.get_feature_names())
term_matrix.head()
可以看到,那些數字全都不見了。特征數量從單一詞表法去除停用詞之后的7144個,變成了1864個。
你可能會覺得,太可惜了吧?好容易分出來的詞,就這么扔了?
要知道,特征多,絕不一定是好事兒。
尤其是噪聲大量混入時,會顯著影響你模型的效能。
好了,評論數據訓練集已經特征向量化了。下面我們要利用生成的特征矩陣來訓練模型了。
我們的分類模型,采用樸素貝葉斯(Multinomial naive bayes)。
from sklearn.naive_bayes import MultinomialNB
nb = MultinomialNB()
注意我們的數據處理流程是這樣的:
- 特征向量化;
- 樸素貝葉斯分類。
如果每次修改一個參數,或者換用測試集,我們都需要重新運行這么多的函數,肯定是一件效率不高,且令人頭疼的事兒。而且只要一復雜,出現錯誤的幾率就會增加。
幸好,Scikit-learn給我們提供了一個功能,叫做管道(pipeline),可以方便解決這個問題。
它可以幫助我們,把這些順序工作連接起來,隱藏其中的功能順序關聯,從外部一次調用,就能完成順序定義的全部工作。
使用很簡單,我們就把 vect 和 nb 串聯起來,叫做pipe。
from sklearn.pipeline import make_pipeline
pipe = make_pipeline(vect, nb)
看看它都包含什么步驟:
pipe.steps
看,我們剛才做的工作,都在管道里面了。我們可以把管道當成一個整體模型來調用。
下面一行語句,就可以把未經特征向量化的訓練集內容輸入,做交叉驗證,算出模型分類準確率的均值。
from sklearn.cross_validation import cross_val_score
cross_val_score(pipe, X_train.cutted_comment, y_train, cv=5, scoring='accuracy').mean()
咱們的模型在訓練中的準確率如何呢?
0.820687244673089
這個結果,還是不錯的。
回憶一下,總體的正向和負向情感,各占了數據集的一半。
如果我們建立一個“笨模型”(dummy model),即所有的評論,都當成正向(或者負向)情感,準確率多少?
對,50%。
目前的模型準確率,遠遠超出這個數值。超出的這30%多,其實就是評論信息為模型帶來的確定性。
但是,不要忘了,我們不能光拿訓練集來說事兒,對吧?下面咱們給模型來個考試。
我們用訓練集,把模型擬合出來。
pipe.fit(X_train.cutted_comment, y_train)
然后,我們在測試集上,對情感分類標記進行預測。
pipe.predict(X_test.cutted_comment)
這一大串0和1,你看得是否眼花繚亂?
沒關系,scikit-learn給我們提供了非常多的模型性能測度工具。
我們先把預測結果保存到y_pred
。
y_pred = pipe.predict(X_test.cutted_comment)
讀入 scikit-learn 的測量工具集。
from sklearn import metrics
我們先來看看測試準確率:
metrics.accuracy_score(y_test, y_pred)
0.86
這個結果是不是讓你很吃驚?沒錯,模型面對沒有見到的數據,居然有如此高的情感分類準確性。
對于分類問題,光看準確率有些不全面,咱們來看看混淆矩陣。
metrics.confusion_matrix(y_test, y_pred)
array([[194, 43],
[ 27, 236]])
混淆矩陣中的4個數字,分別代表:
- TP: 本來是正向,預測也是正向的;
- FP: 本來是負向,預測卻是正向的;
- FN: 本來是正向,預測卻是負向的;
- TN: 本來是負向,預測也是負向的。
下面這張圖(來自 https://goo.gl/5cYGZd )應該能讓你更為清晰理解混淆矩陣的含義:
寫到這兒,你大概能明白咱們模型的性能了。
但是總不能只把咱們訓練出的模型和無腦“笨模型”去對比吧?這也太不公平了!
下面,我們把老朋友 SnowNLP 呼喚出來,做個對比。
如果你把它給忘了,請復習《如何用Python做情感分析?》
from snownlp import SnowNLP
def get_sentiment(text):
return SnowNLP(text).sentiments
我們利用測試集評論原始數據,讓 SnowNLP 跑一遍,獲得結果。
y_pred_snownlp = X_test.comment.apply(get_sentiment)
注意這里有個小問題。 SnowNLP 生成的結果,不是0和1,而是0到1之間的小數。所以我們需要做一步轉換,把0.5以上的結果當作正向,其余當作負向。
y_pred_snownlp_normalized = y_pred_snownlp.apply(lambda x: 1 if x>0.5 else 0)
看看轉換后的前5條 SnowNLP 預測結果:
y_pred_snownlp_normalized[:5]
好了,符合我們的要求。
下面我們先看模型分類準確率:
metrics.accuracy_score(y_test, y_pred_snownlp_normalized)
0.77
與之對比,咱們的測試集分類準確率,可是0.86哦。
我們再來看看混淆矩陣。
metrics.confusion_matrix(y_test, y_pred_snownlp_normalized)
array([[189, 48],
[ 67, 196]])
對比的結果,是 TP 和 TN 兩項上,咱們的模型判斷正確數量,都要超出 SnowNLP。
小結
回顧一下,本文介紹了以下知識點:
- 如何用一袋子詞(bag of words)模型將自然語言語句向量化,形成特征矩陣;
- 如何利用停用詞表、詞頻閾值和標記模式(token pattern)移除不想干的偽特征詞匯,降低模型復雜度。
- 如何選用合適的機器學習分類模型,對詞語特征矩陣做出分類;
- 如何用管道模式,歸并和簡化機器學習步驟流程;
- 如何選擇合適的性能測度工具,對模型的效能進行評估和對比。
希望這些內容能夠幫助你更高效地處理中文文本情感分類工作。
討論
你之前用機器學習做過中文情感分類項目嗎?你是如何去除停用詞的?你使用的分類模型是哪個?獲得的準確率怎么樣?歡迎留言,把你的經驗和思考分享給大家,我們一起交流討論。
喜歡請點贊。還可以微信關注和置頂我的公眾號“玉樹芝蘭”(nkwangshuyi)。
如果你對數據科學感興趣,不妨閱讀我的系列教程索引貼《如何高效入門數據科學?》,里面還有更多的有趣問題及解法。