1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        一文概覽NLP算法(Python)

        共 9153字,需瀏覽 19分鐘

         ·

        2022-05-25 20:44

        一、自然語言處理(NLP)簡介

        NLP,自然語言處理就是用計算機來分析和生成自然語言(文本、語音),目的是讓人類可以用自然語言形式跟計算機系統(tǒng)進行人機交互,從而更便捷、有效地進行信息管理。

        NLP是人工智能領域歷史較為悠久的領域,但由于語言的復雜性(語言表達多樣性/歧義/模糊等等),如今的發(fā)展及收效相對緩慢。比爾·蓋茨曾說過,"NLP是 AI 皇冠上的明珠。" 在光鮮絢麗的同時,卻可望而不可及(...)。

        為了揭開NLP的神秘面紗,本文接下來會梳理下NLP流程、主要任務及算法,并最終落到實際NLP項目(經(jīng)典的文本分類任務的實戰(zhàn))。順便說一句,個人水平有限,不足之處還請留言指出~~

        二、NLP主要任務及技術

        NLP任務可以大致分為詞法分析、句法分析、語義分析三個層面。具體的,本文按照單詞-》句子-》文本做順序展開,并介紹各個層面的任務及對應技術。本節(jié)上半部分的分詞、命名實體識別、詞向量等等可以視為NLP基礎的任務。下半部分的句子關系、文本生成及分類任務可以看做NLP主要的應用任務。這里,貼一張自然語言處理的技術路線圖,介紹了NLP任務及主流模型的分支:

        高清圖可如下路徑下載(原作者graykode):https://github.com/aialgorithm/AiPy/tree/master/Ai%E7%9F%A5%E8%AF%86%E5%9B%BE%E5%86%8C/Ai_Roadmap

        2.1 數(shù)據(jù)清洗 + 分詞(系列標注任務)

        • 數(shù)據(jù)語料清洗。我們拿到文本的數(shù)據(jù)語料(Corpus)后,通常首先要做的是,分析并清洗下文本,主要用正則匹配刪除掉數(shù)字及標點符號(一般這些都是噪音,對于實際任務沒有幫助),做下分詞后,刪掉一些無關的詞(停用詞),對于英文還需要統(tǒng)一下復數(shù)、語態(tài)、時態(tài)等不同形態(tài)的單詞形式,也就是詞干/詞形還原。

        • 分詞。即劃分為詞單元(token),是一個常見的序列標注任務。對于英文等拉丁語系的語句分詞,天然可以通過空格做分詞,對于中文語句,由于中文詞語是連續(xù)的,可以用結巴分詞(基于trie tree+維特比等算法實現(xiàn)最大概率的詞語切分)等工具實現(xiàn)。

        import?jieba
        jieba.lcut("我的地址是上海市松江區(qū)中山街道華光藥房")

        >>>?['我',?'的',?'地址',?'是',?'上海市',?'松江區(qū)',?'中山',?'街道',?'華光',?'藥房']

        • 英文分詞后的詞干/詞形等還原(去除時態(tài) 語態(tài)及復數(shù)等信息,統(tǒng)一為一個“單詞”形態(tài))。這并不是必須的,還是根據(jù)實際任務是否需要保留時態(tài)、語態(tài)等信息,有WordNetLemmatizer、 SnowballStemmer等方法。

        • 分詞及清洗文本后,還需要對照前后的效果差異,在做些微調(diào)。這里可以統(tǒng)計下個單詞的頻率、句長等指標,還可以通過像詞云等工具做下可視化~

        from?wordcloud?import?WordCloud
        ham_msg_cloud?=?WordCloud(width?=520,?height?=260,max_font_size=50,?background_color?="black",?colormap='Blues').generate(原文本語料)
        plt.figure(figsize=(16,10))
        plt.imshow(ham_msg_cloud,?interpolation='bilinear')
        plt.axis('off')?#?turn?off?axis
        plt.show()

        2.2 詞性標注(系列標注任務)

        詞性標注是對句子中的成分做簡單分析,區(qū)分出分名詞、動詞、形容詞之類。對于句法分析、信息抽取的任務,經(jīng)過詞性標注后的文本會帶來很大的便利性(其他方面的應用好像比較少)。

        常用的詞性標注有基于規(guī)則、統(tǒng)計以及深度學習的方法,像HanLP、結巴分詞等工具都有這個功能。

        2.3 命名實體識別(系列標注任務)

        命名實體識別(Named Entity Recognition,簡稱NER)是一個有監(jiān)督的系列標注任務,又稱作“專名識別”,是指識別文本中具有特定意義的實體,主要包括人名、地名、機構名、時間、專有名詞等關鍵信息。通過NER識別出一些關鍵的人名、地名就可以很方便地提取出“某人去哪里,做什么事的信息”,很方便信息提取、問答系統(tǒng)等任務。NER主流的模型實現(xiàn)有BiLSTM-CRF、Bert-CRF,如下一個簡單的中文ner項目:https://github.com/Determined22/zh-NER-TF

        2.4 詞向量(表示學習)

        對于自然語言文本,計算機無法理解詞后面的含義。輸入模型前,首先要做的就是詞的數(shù)值化表示,常用的轉化方式有2種:One-hot編碼、詞嵌入分布式方法。

        • One-hot編碼:最簡單的表示方法某過于onehot表示,每個單詞是否出現(xiàn)就用一位數(shù)單獨展示。進一步,句子的表示也就是累加每個單詞的onehot,也就是常說的句子的詞袋模型(bow)表示。
        ##?詞袋表示
        from?sklearn.feature_extraction.text?import?CountVectorizer
        bow?=?CountVectorizer(
        ????????????????analyzer?=?'word',
        ????????????????strip_accents?=?'ascii',
        ????????????????tokenizer?=?[],
        ????????????????lowercase?=?True,
        ????????????????max_features?=?100,?
        ????????????????)
        • 詞嵌入分布式表示:自然語言的單詞數(shù)是成千上萬的,One-hot編碼會有高維、詞語間無聯(lián)系的缺陷。這時有一種更有效的方法就是——詞嵌入分布式表示,通過神經(jīng)網(wǎng)絡學習構造一個低維、稠密,隱含詞語間關系的向量表示。常見有Word2Vec、Fasttext、Bert等模型學習每個單詞的向量表示,在表示學習后相似的詞匯在向量空間中是比較接近的。
        #?Fasttext?embed模型
        from?gensim.models?import?FastText,word2vec

        model?=?FastText(text,??size=100,sg=1,?window=3,?min_count=1,?iter=10,?min_n=3,?max_n=6,word_ngrams=1,workers=12)
        print(model.wv['hello'])?#?詞向量
        model.save('./data/fasttext100dim')

        特別地,正因為Bert等大規(guī)模自監(jiān)督預訓練方法,又為NLP帶來了春天~

        • 對于學習后的詞表示向量,還可以通過重要程度進行特征加權,合適的加權方法對于任務可以有不錯的提升效果。常用的有卡方chi2、TF-IDF等加權方法。TF-IDF是一種基于統(tǒng)計的方法,其核心思想是假設字詞的重要性與其在某篇文章中出現(xiàn)的比例成正比,與其在其他文章中出現(xiàn)的比例成反比。
        #?TF-IDF可以直接調(diào)用sklearn
        from?sklearn.feature_extraction.text?import?TfidfTransformer

        2.5 句法、語義依存分析

        句法、語義依存分析是傳統(tǒng)自然語言的基礎句子級的任務,語義依存分析是指在句子結構中分析實詞和實詞之間的語義關系,這種關系是一種事實上或邏輯上的關系,且只有當詞語進入到句子時才會存在。語義依存分析的目的即回答句子的”Who did what to whom when and where”的問題。例如句子“張三昨天告訴李四一個秘密”,語義依存分析可以回答四個問題,即誰告訴了李四一個秘密,張三告訴誰一個秘密,張三什么時候告訴李四一個秘密,張三告訴李四什么。

        傳統(tǒng)的自然語言處理多是參照了語言學家對于自然語言的歸納總結,通過句法、語義分析可以挖掘出詞語間的聯(lián)系(主謂賓、施事受事等關系),用于制定文本規(guī)則、信息抽取(如正則匹配疊加語義規(guī)則應用于知識抽取或者構造特征)??梢詤⒖約pacy庫、哈工大NLP的示例:http://ltp.ai/demo.html

        隨著深度學習技術RNN/LSTM等強大的時序模型(sequential modeling)和詞嵌入方法的普及,能夠在一定程度上刻畫句子的隱含語法結構,學習到上下文信息,已經(jīng)逐漸取代了詞法、句法等傳統(tǒng)自然語言處理流程。

        2.6 相似度算法(句子關系的任務)

        自然語言處理任務中,我們經(jīng)常需要判斷兩篇文檔的相似程度(句子關系),比如檢索系統(tǒng)輸出最相關的文本,推薦系統(tǒng)推薦相似的文章。文本相似度匹配常用到的方法有:文本編輯距離、WMD、 BM2.5、詞向量相似度 、Approximate Nearest Neighbor以及一些有監(jiān)督的(神經(jīng)網(wǎng)絡)模型判斷文本間相似度。

        2.7 文本分類任務

        文本分類是經(jīng)典的NLP任務,就是將文本系列對應預測到類別。

        • 一種是輸入序列輸出這整個序列的類別,如短信息、微博分類、意圖識別等。
        • 另一種是輸入序列輸出序列上每個位置的類別,上文提及的系列標注可以看做為詞粒度的一種分類任務,如實體命名識別。

        分類任務使用預訓練+(神經(jīng)網(wǎng)絡)分類模型的端對端學習是主流,深度學習學習特征的表達然后進行分類,大大減少人工的特征。但以實際項目中的經(jīng)驗來看,對于一些困難任務(任務的噪聲大),加入些人工的特征工程還是很有必要的。

        2.8 文本生成任務

        文本生成也就是由類別生成序列 或者 由序列到序列的預測任務。按照不同的輸入劃分,文本自動生成可包括文本到文本的生成(text-to-text ?generation)、意義到文本的生成(meaning-to-text ?generation)、數(shù)據(jù)到文本的生成(data-to-text ?generation)以及圖像到文本的生成(image-to-text generation)等。具體應用如機器翻譯、文本摘要理解、閱讀理解、閑聊對話、寫作、看圖說話。常用的模型如RNN、CNN、seq2seq、Transformer。

        同樣的,基于大規(guī)模預訓練模型的文本生成也是一大熱門,可見《A Survey of Pretrained Language Models Based Text Generation》

        三、垃圾短信文本分類實戰(zhàn)

        3.1 讀取短信文本數(shù)據(jù)并展示

        本項目是通過有監(jiān)督的短信文本,學習一個垃圾短信文本分類模型。數(shù)據(jù)樣本總的有5572條,label有spam(垃圾短信)和ham兩種,是一個典型類別不均衡的二分類問題。

        #?源碼可見https://github.com/aialgorithm/Blog
        import?pandas?as?pd
        import?numpy?as?np
        import??matplotlib.pyplot?as?plt

        spam_df?=?pd.read_csv('./data/spam.csv',?header=0,?encoding="ISO-8859-1")

        #?數(shù)據(jù)展示
        _,?ax?=?plt.subplots(1,2,figsize=(10,5))
        spam_df['label'].value_counts().plot(ax=ax[0],?kind="bar",?rot=90,?title='label');
        spam_df['label'].value_counts().plot(ax=ax[1],?kind="pie",?rot=90,?title='label',?ylabel='');
        print("Dataset?size:?",?spam_df.shape)

        spam_df.head(5)

        3.2 數(shù)據(jù)清洗預處理

        數(shù)據(jù)清洗在于去除一些噪聲信息,這里對短信文本做按空格分詞,統(tǒng)一大小寫,清洗非英文字符,去掉停用詞并做了詞干還原??紤]到短信文本里面的數(shù)字位數(shù)可能有一定的含義,這里將數(shù)字替換為‘x’的處理。最后,將標簽統(tǒng)一為數(shù)值(0、1)是否垃圾短信。

        #?導入相關的庫
        import?nltk
        from?nltk?import?word_tokenize
        from?nltk.corpus?import?stopwords
        from?nltk.data?import?load
        from?nltk.stem?import?SnowballStemmer
        from?string?import?punctuation

        import?re??#?正則匹配
        stop_words?=?set(stopwords.words('english'))
        non_words?=?list(punctuation)


        #?詞形、詞干還原
        #?from?nltk.stem?import?WordNetLemmatizer
        #?wnl?=?WordNetLemmatizer()
        stemmer?=?SnowballStemmer('english')
        def?stem_tokens(tokens,?stemmer):
        ????stems?=?[]
        ????for?token?in?tokens:
        ????????stems.append(stemmer.stem(token))
        ????return?stems

        ###?清除非英文詞匯并替換數(shù)值x
        def?clean_non_english_xdig(txt,isstem=True,?gettok=True):
        ????txt?=?re.sub('[0-9]',?'x',?txt)?#?去數(shù)字替換為x
        ????txt?=?txt.lower()?#?統(tǒng)一小寫
        ????txt?=?re.sub('[^a-zA-Z]',?'?',?txt)?#去除非英文字符并替換為空格
        ????word_tokens?=?word_tokenize(txt)?#?分詞
        ????if?not?isstem:?#是否做詞干還原
        ????????filtered_word?=?[w?for?w?in?word_tokens?if?not?w?in?stop_words]??#?刪除停用詞
        ????else:
        ????????filtered_word?=?[stemmer.stem(w)?for?w?in?word_tokens?if?not?w?in?stop_words]???#?刪除停用詞及詞干還原
        ????if?gettok:???#返回為字符串或分詞列表
        ????????return?filtered_word
        ????else:
        ????????return?"?".join(filtered_word)

        spam_df['token']?=?spam_df.message.apply(lambda?x:clean_non_english_xdig(x))
        spam_df.head(3)

        #?數(shù)據(jù)清洗
        spam_df['token']?=?spam_df.message.apply(lambda?x:clean_non_english_xdig(x))

        #?標簽整數(shù)編碼
        spam_df['label']?=?(spam_df.label=='spam').astype(int)

        spam_df.head(3)

        3.3 fasttext詞向量表示學習

        我們需要將單詞文本轉化為數(shù)值的詞向量才能輸入模型。詞向量表示常用的詞袋、fasttext、bert等方法,這里訓練的是fasttext,模型的主要輸入?yún)?shù)是,輸入分詞后的語料(通常訓練語料越多越好,當現(xiàn)有語料有限時候,直接拿github上合適的大規(guī)模預訓練模型來做詞向量也是不錯的選擇),詞向量的維度size(一個經(jīng)驗的詞向量維度設定是,dim > 8.33 logN, N為詞匯表的大小,當維度dim足夠大才能表達好這N規(guī)模的詞匯表的含義??蓞⒖肌? 最小熵原理(六):詞向量的維度應該怎么選擇?By 蘇劍林》)。語料太大的時候可以使用workers開啟多進程訓練(其他參數(shù)及詞表示學習原理后續(xù)會專題介紹,也可以自行了解)。

        #?訓練詞向量?Fasttext?embed模型
        from?gensim.models?import?FastText,word2vec

        fmodel?=?FastText(spam_df.token,??size=100,sg=1,?window=3,?min_count=1,?iter=10,?min_n=3,?max_n=6,word_ngrams=1,workers=12)
        print(fmodel.wv['hello'])?#?輸出hello的詞向量
        #?fmodel.save('./data/fasttext100dim')

        按照句子所有的詞向量取平均,為每一句子生成句向量。

        fmodel?=?FastText.load('./data/fasttext100dim')

        #對每個句子的所有詞向量取均值,來生成一個句子的vector
        def?build_sentence_vector(sentence,w2v_model,size=100):
        ????sen_vec=np.zeros((size,))
        ????count=0
        ????for?word?in?sentence:
        ????????try:
        ????????????sen_vec+=w2v_model[word]#.reshape((1,size))
        ????????????count+=1
        ????????except?KeyError:
        ????????????continue
        ????if?count!=0:
        ????????sen_vec/=count
        ????return?sen_vec

        #?句向量
        sents_vec?=?[]
        for?sent?in?spam_df['token']:
        ????sents_vec.append(build_sentence_vector(sent,fmodel,size=100))
        ????????
        print(len(sents_vec))

        3.4 訓練文本分類模型

        示例采用的fasttext embedding + lightgbm的二分類模型,類別不均衡使用lgb代價敏感學習解決(即class_weight='balanced'),超參數(shù)是手動簡單配置的,可以自行搜索下較優(yōu)超參數(shù)。

        ###?訓練文本分類模型
        from?sklearn.model_selection?import?train_test_split
        from?lightgbm?import?LGBMClassifier
        from?sklearn.linear_model?import?LogisticRegression

        train_x,?test_x,?train_y,?test_y?=?train_test_split(sents_vec,?spam_df.label,test_size=0.2,shuffle=True,random_state=42)
        result?=?[]
        clf?=?LGBMClassifier(class_weight='balanced',n_estimators=300,?num_leaves=64,?reg_alpha=?1,reg_lambda=?1,random_state=42)
        #clf?=?LogisticRegression(class_weight='balanced',random_state=42)

        clf.fit(train_x,train_y)

        import?pickle
        #?保存模型
        pickle.dump(clf,?open('./saved_models/spam_clf.pkl',?'wb'))

        #?加載模型
        model?=?pickle.load(open('./saved_models/spam_clf.pkl',?'rb'))

        3.5 模型評估

        訓練集測試集按0.2劃分,分布驗證訓練集測試集的AUC、F1score等指標,均有不錯的表現(xiàn)。

        from?sklearn.metrics?import?auc,roc_curve,f1_score,precision_score,recall_score
        def?model_metrics(model,?x,?y,tp='auc'):
        ????"""?評估?"""
        ????yhat?=?model.predict(x)
        ????yprob?=?model.predict_proba(x)[:,1]
        ????fpr,tpr,_?=?roc_curve(y,?yprob,pos_label=1)
        ????metrics?=?{'AUC':auc(fpr,?tpr),'KS':max(tpr-fpr),
        ???????????????'f1':f1_score(y,yhat),'P':precision_score(y,yhat),'R':recall_score(y,yhat)}
        ????
        ????roc_auc?=?auc(fpr,?tpr)

        ????plt.plot(fpr,?tpr,?'k--',?label='ROC?(area?=?{0:.2f})'.format(roc_auc),?lw=2)

        ????plt.xlim([-0.05,?1.05])??#?設置x、y軸的上下限,以免和邊緣重合,更好的觀察圖像的整體
        ????plt.ylim([-0.05,?1.05])
        ????plt.xlabel('False?Positive?Rate')
        ????plt.ylabel('True?Positive?Rate')??#?可以使用中文,但需要導入一些庫即字體
        ????plt.title('ROC?Curve')
        ????plt.legend(loc="lower?right")


        ????return?metrics

        print('train?',model_metrics(clf,??train_x,?train_y,tp='ks'))
        print('test?',model_metrics(clf,?test_x,test_y,tp='ks'))

        ——The ?End——

        分享

        收藏

        點贊

        在看


        瀏覽 29
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            黄色网址 | 日韩国产区 | 97色色吧 | 中国女人如毛片 | 欧美久久久久久 | 国产三香港三韩国三级 | 精品美女| 操嫩逼无码 | 靠逼免费看 | 国产乱国产乱片 |