一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

python 中文文本分類

 qqcat5 2019-07-15

一,中文文本分類流程:

1,預(yù)處理

2,中文分詞

3,結(jié)構(gòu)化表示--構(gòu)建詞向量空間

4,權(quán)重策略--TF-IDF

5,分類器

6,評價(jià)

二,具體細(xì)節(jié)

1,預(yù)處理。希望得到這樣的目標(biāo):

1.1得到訓(xùn)練集語料庫

即已經(jīng)分好類的文本資料(例如:語料庫里是一系列txt文章,這些文章按照主題歸入到不同分類的目錄中,如 .\art\21.txt)

推薦語料庫:復(fù)旦中文文本分類語料庫,下載鏈接:http://download.csdn.net/detail/github_36326955/9747927

將下載的語料庫解壓后,請自己修改文件名和路徑,例如路徑可以設(shè)置為 ./train_corpus/,

其下則是各個(gè)類別目錄如:./train_corpus/C3-Art,……,\train_corpus\C39-Sports

1.2得到測試語料庫

也是已經(jīng)分好類的文本資料,與1.1類型相同,只是里面的文檔不同,用于檢測算法的實(shí)際效果。測試預(yù)料可以從1.1中的訓(xùn)練預(yù)料中隨機(jī)抽取,也可以下載獨(dú)立的測試語料庫,復(fù)旦中文文本分類語料庫測試集鏈接:http://download.csdn.net/detail/github_36326955/9747929

路徑修改參考1.1,例如可以設(shè)置為 ./test_corpus/

1.3其他

你可能希望從自己爬取到的網(wǎng)頁等內(nèi)容中獲取新文本,用本節(jié)內(nèi)容進(jìn)行實(shí)際的文本分類,這時(shí)候,你可能需要將html標(biāo)簽去除來獲取文本格式的文檔,這里提供一個(gè)基于python 和lxml的樣例代碼:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@version: python2.7.8 
@author: XiangguoSun
@contact: sunxiangguodut@qq.com
@file: html_demo.py
@time: 2017/2/6 12:25
@software: PyCharm
"""
import sys
from lxml import html
# 設(shè)置utf-8 unicode環(huán)境
reload(sys)
sys.setdefaultencoding('utf-8')

def html2txt(path):
    with open(path,"rb") as f:
        content=f.read() 
    r'''
    上面兩行是python2.6以上版本增加的語法,省略了繁瑣的文件close和try操作
    2.5版本需要from __future__ import with_statement
    新手可以參考這個(gè)鏈接來學(xué)習(xí)http:///archives/325
    '''
    page = html.document_fromstring(content) # 解析文件
    text = page.text_content() # 去除所有標(biāo)簽
    return text

if __name__  =="__main__":
    # htm文件路徑,以及讀取文件
    path = "1.htm"
    text=html2txt(path)
    print text	 # 輸出去除標(biāo)簽后解析結(jié)果




2,中文分詞。

本小節(jié)的目標(biāo)是:

1,告訴你中文分詞的實(shí)際操作。

2,告訴你中文分詞的算法。





2.1概述

第1小節(jié)預(yù)處理中的語料庫都是沒有分詞的原始語料(即連續(xù)的句子,而后面的工作需要我們把文本分為一個(gè)個(gè)單詞),現(xiàn)在需要對這些文本進(jìn)行分詞,只有這樣,才能在 基于單詞的基礎(chǔ)上,對文檔進(jìn)行結(jié)構(gòu)化表示。

中文分詞有其特有的難點(diǎn)(相對于英文而言),最終完全解決中文分詞的算法是基于概率圖模型的條件隨機(jī)場(CRF)。(可以參考博主的另一篇博文)

當(dāng)然,在實(shí)際操作中,即使你對于相關(guān)算法不甚了解,也不影響你的操作,中文分詞的工具有很多。但是比較著名的幾個(gè)都是基于java的,這里推薦python的第三方庫jieba(所采用的算法就是條件隨機(jī)場)。對于非專業(yè)文檔綽綽有余。如果你有強(qiáng)迫癥,希望得到更高精度的分詞工具,可以使用開源項(xiàng)目Anjs(基于java),你可以將這個(gè)開源項(xiàng)目與python整合。

關(guān)于分詞庫的更多討論可以參考這篇文章:https://www.zhihu.com/question/19651613

你可以通過pip安裝jieba:打開cmd,切換到目錄  .../python/scripts/,執(zhí)行命令:pip install jieba

或者你也可以在集成開發(fā)平臺(tái)上安裝jieba,例如,如果你用的是pycharm,可以點(diǎn)擊file-settings-project:xxx-Projuect Interpreter.其他平臺(tái)也都有類似的安裝方法。

2.2分詞操作

不要擔(dān)心下面的代碼你看不懂,我會(huì)非常詳細(xì)的進(jìn)行講解,確保python入門級(jí)別水平的人都可以看懂:

2.2.1

首先講解jieba分詞使用方法(詳細(xì)的和更進(jìn)一步的,可以參考這個(gè)鏈接):

jieba.cut 方法接受三個(gè)輸入?yún)?shù): 需要分詞的字符串;cut_all 參數(shù)用來控制是否采用全模式;HMM 參數(shù)用來控制是否使用 HMM 模型
jieba.cut_for_search 方法接受兩個(gè)參數(shù):需要分詞的字符串;是否使用 HMM 模型。該方法適合用于搜索引擎構(gòu)建倒排索引的分詞,粒度比較細(xì)
待分詞的字符串可以是 unicode 或 UTF-8 字符串、GBK 字符串。注意:不建議直接輸入 GBK 字符串,可能無法預(yù)料地錯(cuò)誤解碼成 UTF-8
jieba.cut 以及 jieba.cut_for_search 返回的結(jié)構(gòu)都是一個(gè)可迭代的 generator,可以使用 for 循環(huán)來獲得分詞后得到的每一個(gè)詞語(unicode),或者用
jieba.lcut 以及 jieba.lcut_for_search 直接返回 list
jieba.Tokenizer(dictionary=DEFAULT_DICT) 新建自定義分詞器,可用于同時(shí)使用不同詞典。jieba.dt 為默認(rèn)分詞器,所有全局分詞相關(guān)函數(shù)都是該分詞器的映射。
實(shí)例代碼:

import jieba

seg_list = jieba.cut("我來到北京清華大學(xué)", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list))  # 全模式

seg_list = jieba.cut("我來到北京清華大學(xué)", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list))  # 精確模式

seg_list = jieba.cut("他來到了網(wǎng)易杭研大廈")  # 默認(rèn)是精確模式
print(", ".join(seg_list))

seg_list = jieba.cut_for_search("小明碩士畢業(yè)于中國科學(xué)院計(jì)算所,后在日本京都大學(xué)深造")  # 搜索引擎模式
print(", ".join(seg_list))


輸出:

【全模式】: 我/ 來到/ 北京/ 清華/ 清華大學(xué)/ 華大/ 大學(xué)

【精確模式】: 我/ 來到/ 北京/ 清華大學(xué)

【新詞識(shí)別】:他, 來到, 了, 網(wǎng)易, 杭研, 大廈    (此處,“杭研”并沒有在詞典中,但是也被Viterbi算法識(shí)別出來了)

【搜索引擎模式】: 小明, 碩士, 畢業(yè), 于, 中國, 科學(xué), 學(xué)院, 科學(xué)院, 中國科學(xué)院, 計(jì)算, 計(jì)算所, 后, 在, 日本, 京都, 大學(xué), 日本京都大
2.2.2

接下來,我們將要通過python編程,來將1.1節(jié)中的 ./train_corpus/原始訓(xùn)練語料庫和1.2節(jié)中的./test_corpus/原始測試語料庫進(jìn)行分詞,分詞后保存的路徑可以設(shè)置為

./train_corpus_seg/和./test_corpus_seg/

代碼如下,思路很簡單,就是遍歷所有的txt文本,然后將每個(gè)文本依次進(jìn)行分詞。你唯一需要注意的就是寫好自己的路徑,不要出錯(cuò)。下面的代碼已經(jīng)給出了非常詳盡的解釋,初學(xué)者也可以看懂。如果你還沒有明白,或者在運(yùn)行中出現(xiàn)問題(其實(shí)根本不可能出現(xiàn)問題,我寫的代碼,質(zhì)量很高的。。。),都可以發(fā)郵件給我,郵件地址在代碼中,或者在博文下方評論中給出。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@version: python2.7.8 
@author: XiangguoSun
@contact: sunxiangguodut@qq.com
@file: corpus_segment.py
@time: 2017/2/5 15:28
@software: PyCharm
"""
import sys
import os
import jieba
# 配置utf-8輸出環(huán)境
reload(sys)
sys.setdefaultencoding('utf-8')
# 保存至文件
def savefile(savepath, content):
    with open(savepath, "wb") as fp:
        fp.write(content)
    '''
    上面兩行是python2.6以上版本增加的語法,省略了繁瑣的文件close和try操作
    2.5版本需要from __future__ import with_statement
    新手可以參考這個(gè)鏈接來學(xué)習(xí)http:///archives/325
    '''
# 讀取文件
def readfile(path):
    with open(path, "rb") as fp:
        content = fp.read()
    return content

def corpus_segment(corpus_path, seg_path):
    '''
    corpus_path是未分詞語料庫路徑
    seg_path是分詞后語料庫存儲(chǔ)路徑
    '''
    catelist = os.listdir(corpus_path)  # 獲取corpus_path下的所有子目錄
    '''
    其中子目錄的名字就是類別名,例如:
    train_corpus/art/21.txt中,'train_corpus/'是corpus_path,'art'是catelist中的一個(gè)成員
    '''

    # 獲取每個(gè)目錄(類別)下所有的文件
    for mydir in catelist:
        '''
        這里mydir就是train_corpus/art/21.txt中的art(即catelist中的一個(gè)類別)
        '''
        class_path = corpus_path + mydir + "/"  # 拼出分類子目錄的路徑如:train_corpus/art/
        seg_dir = seg_path + mydir + "/"  # 拼出分詞后存貯的對應(yīng)目錄路徑如:train_corpus_seg/art/

        if not os.path.exists(seg_dir):  # 是否存在分詞目錄,如果沒有則創(chuàng)建該目錄
            os.makedirs(seg_dir)

        file_list = os.listdir(class_path)  # 獲取未分詞語料庫中某一類別中的所有文本
        '''
        train_corpus/art/中的
        21.txt,
        22.txt,
        23.txt
        ...
        file_list=['21.txt','22.txt',...]
        '''
        for file_path in file_list:  # 遍歷類別目錄下的所有文件
            fullname = class_path + file_path  # 拼出文件名全路徑如:train_corpus/art/21.txt
            content = readfile(fullname)  # 讀取文件內(nèi)容
            '''此時(shí),content里面存貯的是原文本的所有字符,例如多余的空格、空行、回車等等,
            接下來,我們需要把這些無關(guān)痛癢的字符統(tǒng)統(tǒng)去掉,變成只有標(biāo)點(diǎn)符號(hào)做間隔的緊湊的文本內(nèi)容
            '''
            content = content.replace("\r\n", "")  # 刪除換行
            content = content.replace(" ", "")#刪除空行、多余的空格
            content_seg = jieba.cut(content)  # 為文件內(nèi)容分詞
            savefile(seg_dir + file_path, " ".join(content_seg))  # 將處理后的文件保存到分詞后語料目錄

    print "中文語料分詞結(jié)束?。?!"

'''
如果你對if __name__=="__main__":這句不懂,可以參考下面的文章
http://imoyao.lofter.com/post/3492bc_bd0c4ce
簡單來說如果其他python文件調(diào)用這個(gè)文件的函數(shù),或者把這個(gè)文件作為模塊
導(dǎo)入到你的工程中時(shí),那么下面的代碼將不會(huì)被執(zhí)行,而如果單獨(dú)在命令行中
運(yùn)行這個(gè)文件,或者在IDE(如pycharm)中運(yùn)行這個(gè)文件時(shí)候,下面的代碼才會(huì)運(yùn)行。
即,這部分代碼相當(dāng)于一個(gè)功能測試。
如果你還沒懂,建議你放棄IT這個(gè)行業(yè)。
'''
if __name__=="__main__":
    #對訓(xùn)練集進(jìn)行分詞
    corpus_path = "./train_corpus/"  # 未分詞分類語料庫路徑
    seg_path = "./train_corpus_seg/"  # 分詞后分類語料庫路徑
    corpus_segment(corpus_path,seg_path)

    #對測試集進(jìn)行分詞
    corpus_path = "./test_corpus/"  # 未分詞分類語料庫路徑
    seg_path = "./test_corpus_seg/"  # 分詞后分類語料庫路徑
    corpus_segment(corpus_path,seg_path)




截止目前,我們已經(jīng)得到了分詞后的訓(xùn)練集語料庫和測試集語料庫,下面我們要把這兩個(gè)數(shù)據(jù)集表示為變量,從而為下面程序調(diào)用提供服務(wù)。我們采用的是Scikit-Learn庫中的Bunch數(shù)據(jù)結(jié)構(gòu)來表示這兩個(gè)數(shù)據(jù)集。你或許對于Scikit-Learn和Bunch并不是特別了解,而官方的技術(shù)文檔有兩千多頁你可能也沒耐心去看,好在你們有相國大人。下面我們 以這兩個(gè)數(shù)據(jù)集為背景,對Bunch做一個(gè)非常通俗的講解,肯定會(huì)讓你一下子就明白。

首先來看看Bunch:

Bunch這玩意兒,其實(shí)就相當(dāng)于python中的字典。你往里面?zhèn)魇裁?,它就存什么?/p>

好了,解釋完了。

是不是很簡單?

在本篇博文中,你對Bunch能夠有這種層次的理解,就足夠了。如果你想繼續(xù)詳細(xì)透徹的理解Bunch,請見博主的另一篇博文《暫時(shí)還沒寫,寫完在這里更新鏈接》

接下來,讓我們看看的我們的數(shù)據(jù)集(訓(xùn)練集)有哪些信息:

1,類別,也就是所有分類類別的集合,即我們./train_corpus_seg/和./test_corpus_seg/下的所有子目錄的名字。我們在這里不妨把它叫做target_name(這是一個(gè)列表)

2,文本文件名。例如./train_corpus_seg/art/21.txt,我們可以把所有文件名集合在一起做一個(gè)列表,叫做filenames

3,文本標(biāo)簽(就是文本的類別),不妨叫做label(與2中的filenames一一對應(yīng))

例如2中的文本“21.txt”在./train_corpus_seg/art/目錄下,則它的標(biāo)簽就是art。

文本標(biāo)簽與1中的類別區(qū)別在于:文本標(biāo)簽集合里面的元素就是1中類別,而文本標(biāo)簽集合的元素是可以重復(fù)的,因?yàn)?/train_corpus_seg/art/目錄下有好多文本,不是嗎?相應(yīng)的,1中的類別集合元素顯然都是獨(dú)一無二的類別。

4,文本內(nèi)容(contens)。

上一步代碼我們已經(jīng)成功的把文本內(nèi)容進(jìn)行了分詞,并且去除掉了所有的換行,得到的其實(shí)就是一行詞袋(詞向量),每個(gè)文本文件都是一個(gè)詞向量。這里的文本內(nèi)容指的就是這些詞向量。



那么,用Bunch表示,就是:

from sklearn.datasets.base import Bunch

bunch = Bunch(target_name=[],label=[],filenames=[],contents=[]) 



我們在Bunch對象里面創(chuàng)建了有4個(gè)成員:

target_name:是一個(gè)list,存放的是整個(gè)數(shù)據(jù)集的類別集合。

label:是一個(gè)list,存放的是所有文本的標(biāo)簽。

filenames:是一個(gè)list,存放的是所有文本文件的名字。

contents:是一個(gè)list,分詞后文本文件詞向量形式

如果你還沒有明白,看一下下面這個(gè)圖,你總該明白了:

Bunch:



下面,我們將文本文件轉(zhuǎn)為Bunch類形:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@version: python2.7.8 
@author: XiangguoSun
@contact: sunxiangguodut@qq.com
@file: corpus2Bunch.py
@time: 2017/2/7 7:41
@software: PyCharm
"""
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
import os#python內(nèi)置的包,用于進(jìn)行文件目錄操作,我們將會(huì)用到os.listdir函數(shù)
import cPickle as pickle#導(dǎo)入cPickle包并且取一個(gè)別名pickle
'''
事實(shí)上python中還有一個(gè)也叫作pickle的包,與這里的名字相同了,無所謂
關(guān)于cPickle與pickle,請參考博主另一篇博文:
python核心模塊之pickle和cPickle講解
http://blog.csdn.net/github_36326955/article/details/54882506
本文件代碼下面會(huì)用到cPickle中的函數(shù)cPickle.dump
'''
from sklearn.datasets.base import Bunch
#這個(gè)您無需做過多了解,您只需要記住以后導(dǎo)入Bunch數(shù)據(jù)結(jié)構(gòu)就像這樣就可以了。
#今后的博文會(huì)對sklearn做更有針對性的講解


def _readfile(path):
    '''讀取文件'''
    #函數(shù)名前面帶一個(gè)_,是標(biāo)識(shí)私有函數(shù)
    # 僅僅用于標(biāo)明而已,不起什么作用,
    # 外面想調(diào)用還是可以調(diào)用,
    # 只是增強(qiáng)了程序的可讀性
    with open(path, "rb") as fp:#with as句法前面的代碼已經(jīng)多次介紹過,今后不再注釋
        content = fp.read()
    return content

def corpus2Bunch(wordbag_path,seg_path):
    catelist = os.listdir(seg_path)# 獲取seg_path下的所有子目錄,也就是分類信息
    #創(chuàng)建一個(gè)Bunch實(shí)例
    bunch = Bunch(target_name=[], label=[], filenames=[], contents=[])
    bunch.target_name.extend(catelist)
    '''
    extend(addlist)是python list中的函數(shù),意思是用新的list(addlist)去擴(kuò)充
    原來的list
    '''
    # 獲取每個(gè)目錄下所有的文件
    for mydir in catelist:
        class_path = seg_path + mydir + "/"  # 拼出分類子目錄的路徑
        file_list = os.listdir(class_path)  # 獲取class_path下的所有文件
        for file_path in file_list:  # 遍歷類別目錄下文件
            fullname = class_path + file_path  # 拼出文件名全路徑
            bunch.label.append(mydir)
            bunch.filenames.append(fullname)
            bunch.contents.append(_readfile(fullname))  # 讀取文件內(nèi)容
            '''append(element)是python list中的函數(shù),意思是向原來的list中添加element,注意與extend()函數(shù)的區(qū)別'''
    # 將bunch存儲(chǔ)到wordbag_path路徑中
    with open(wordbag_path, "wb") as file_obj:
        pickle.dump(bunch, file_obj)
    print "構(gòu)建文本對象結(jié)束?。。?

if __name__ == "__main__":#這個(gè)語句前面的代碼已經(jīng)介紹過,今后不再注釋
    #對訓(xùn)練集進(jìn)行Bunch化操作:
    wordbag_path = "train_word_bag/train_set.dat"  # Bunch存儲(chǔ)路徑
    seg_path = "train_corpus_seg/"  # 分詞后分類語料庫路徑
    corpus2Bunch(wordbag_path, seg_path)

    # 對測試集進(jìn)行Bunch化操作:
    wordbag_path = "test_word_bag/test_set.dat"  # Bunch存儲(chǔ)路徑
    seg_path = "test_corpus_seg/"  # 分詞后分類語料庫路徑
    corpus2Bunch(wordbag_path, seg_path)

3,結(jié)構(gòu)化表示--向量空間模型

在第2節(jié)中,我們對原始數(shù)據(jù)集進(jìn)行了分詞處理,并且通過綁定為Bunch數(shù)據(jù)類型,實(shí)現(xiàn)了數(shù)據(jù)集的變量表示。事實(shí)上在第2節(jié)中,我們通過分詞,已經(jīng)將每一個(gè)文本文件表示為了一個(gè)詞向量了。也許你對于什么是詞向量并沒有清晰的概念,這里有一篇非常棒的文章《Deep Learning in NLP (一)詞向量和語言模型》,簡單來講,詞向量就是詞向量空間里面的一個(gè)向量。

你可以類比為三維空間里面的一個(gè)向量,例如:

如果我們規(guī)定詞向量空間為:(我,喜歡,相國大人),這相當(dāng)于三維空間里面的(x,y,z)只不過這里的x,y,z的名字變成了“我”,“喜歡”,“相國大人”



現(xiàn)在有一個(gè)詞向量是:我喜歡  喜歡相國大人

表示在詞向量空間中就變?yōu)椋海?,2,1),歸一化后可以表示為:(0.166666666667 0.333333333333 0.166666666667)表示在剛才的詞向量空間中就是這樣:



但是在我們第2節(jié)處理的這些文件中,詞向量之間的單詞個(gè)數(shù)并不相同,詞向量的涵蓋的單詞也不盡相同。他們并不在一個(gè)空間里,換句話說,就是他們之間沒有可比性,例如:

詞向量1:我喜歡相國大人,對應(yīng)的詞向量空間是(我,喜歡,相國大人),可以表示為(1,1,1)

詞向量2:她喜歡我,對應(yīng)的詞向量空間是(她,不,喜歡,我),可以表示為(1,1,1,1)

兩個(gè)空間不一樣

因此,接下來我們要做的,就是把所有這些詞向量統(tǒng)一到同一個(gè)詞向量空間中,例如,在上面的例子中,我們可以設(shè)置詞向量空間為(我,喜歡,相國大人,她,不)

這樣,詞向量1和詞向量2分別可以表示為(1,1,1,0,0)和(1,1,0,1,1),這樣兩個(gè)向量就都在同一個(gè)空間里面了??梢赃M(jìn)行比較和各種運(yùn)算了。

也許你已經(jīng)發(fā)現(xiàn)了,這樣做的一個(gè)很糟糕的結(jié)果是,我們要把訓(xùn)練集內(nèi)所有出現(xiàn)過的單詞,都作為一個(gè)維度,構(gòu)建統(tǒng)一的詞向量空間,即使是中等大小的文本集合,向量維度也很輕易就達(dá)到數(shù)十萬維。為了節(jié)省空間,我們首先將訓(xùn)練集中每個(gè)文本中一些垃圾詞匯去掉。所謂的垃圾詞匯,就是指意義模糊的詞,或者一些語氣助詞,標(biāo)點(diǎn)符號(hào)等等,通常他們對文本起不了分類特征的意義。這些垃圾詞匯我們稱之為停用詞。把所有停用詞集合起來構(gòu)成一張停用詞表格,這樣,以后我們處理文本時(shí),就可以從這個(gè)根據(jù)表格,過濾掉文本中的一些垃圾詞匯了。

你可以從這里下載停用詞表:hlt_stop_words.txt

存放在這里路徑中:train_word_bag/hlt_stop_words.txt

下面的程序,目的就是要將訓(xùn)練集所有文本文件(詞向量)統(tǒng)一到同一個(gè)詞向量空間中。值得一提的是,在詞向量空間中,事實(shí)上不同的詞,它的權(quán)重是不同的,它對文本分類的影響力也不同,為此我們希望得到的詞向量空間不是等權(quán)重的空間,而是不同權(quán)重的詞向量空間。我們把帶有不同權(quán)重的詞向量空間叫做“加權(quán)詞向量空間”,也有的技術(shù)文檔將其稱為“加權(quán)向量詞袋”,一個(gè)意思。

現(xiàn)在的問題是,如何計(jì)算不同詞的權(quán)重呢?

4,權(quán)重策略--TF-IDF

什么是TF-IDF?今后有精力我會(huì)在這里更新補(bǔ)充,現(xiàn)在,先給你推薦一篇非常棒的文章《使用scikit-learn工具計(jì)算文本TF-IDF值

下面,我們假定你已經(jīng)對TF-IDF有了最基本的了解。請你動(dòng)動(dòng)你的小腦袋瓜想一想,我們把訓(xùn)練集文本轉(zhuǎn)換成了一個(gè)TF-IDF詞向量空間,姑且叫它為A空間吧。那么我們還有測試集數(shù)據(jù),我們以后實(shí)際運(yùn)用時(shí),還會(huì)有新的數(shù)據(jù),這些數(shù)據(jù)顯然也要轉(zhuǎn)到詞向量空間,那么應(yīng)該和A空間為同一個(gè)空間嗎?

是的。

即使測試集出現(xiàn)了新的詞匯(不是停用詞),即使新的文本數(shù)據(jù)有新的詞匯,只要它不是訓(xùn)練集生成的TF-IDF詞向量空間中的詞,我們就都不予考慮。這就實(shí)現(xiàn)了所有文本詞向量空間“大一統(tǒng)”,也只有這樣,大家才在同一個(gè)世界里。才能進(jìn)行下一步的研究。

下面的程序就是要將訓(xùn)練集所有文本文件(詞向量)統(tǒng)一到同一個(gè)TF-IDF詞向量空間中(或者叫做用TF-IDF算法計(jì)算權(quán)重的有權(quán)詞向量空間)。這個(gè)詞向量空間最終存放在train_word_bag/tfdifspace.dat中。

這段代碼你可能有點(diǎn)看不懂,因?yàn)槲夜烙?jì)你可能比較懶,還沒看過TF-IDF(盡管我剛才已經(jīng)給你推薦那篇文章了)。你只需要明白,它把一大坨訓(xùn)練集數(shù)據(jù)成功的構(gòu)建了一個(gè)TF-IDF詞向量空間,空間的各個(gè)詞都是出自這個(gè)訓(xùn)練集(去掉了停用詞)中,各個(gè)詞的權(quán)值也都一并保存了下來,叫做權(quán)重矩陣。

需要注意的是,你要明白,權(quán)重矩陣是一個(gè)二維矩陣,a[i][j]表示,第i個(gè)詞在第j個(gè)類別中的IF-IDF值(看到這里,我估計(jì)你壓根就沒去看那篇文章,所以你可能到現(xiàn)在也不知道 這是個(gè)啥玩意兒。。。)

請記住權(quán)重矩陣這個(gè)詞,代碼解釋中我會(huì)用到。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@version: python2.7.8 
@author: XiangguoSun
@contact: sunxiangguodut@qq.com
@file: vector_space.py
@time: 2017/2/7 17:29
@software: PyCharm
"""
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
# 引入Bunch類
from sklearn.datasets.base import Bunch
import cPickle as pickle#之前已經(jīng)說過,不再贅述
from sklearn.feature_extraction.text import TfidfVectorizer#這個(gè)東西下面會(huì)講

# 讀取文件
def _readfile(path):
    with open(path, "rb") as fp:
        content = fp.read()
    return content

# 讀取bunch對象
def _readbunchobj(path):
    with open(path, "rb") as file_obj:
        bunch = pickle.load(file_obj)
    return bunch

# 寫入bunch對象
def _writebunchobj(path, bunchobj):
    with open(path, "wb") as file_obj:
        pickle.dump(bunchobj, file_obj)

#這個(gè)函數(shù)用于創(chuàng)建TF-IDF詞向量空間
def vector_space(stopword_path,bunch_path,space_path):

    stpwrdlst = _readfile(stopword_path).splitlines()#讀取停用詞
    bunch = _readbunchobj(bunch_path)#導(dǎo)入分詞后的詞向量bunch對象
    #構(gòu)建tf-idf詞向量空間對象
    tfidfspace = Bunch(target_name=bunch.target_name, label=bunch.label, filenames=bunch.filenames, tdm=[], vocabulary={})
    '''
    在前面幾節(jié)中,我們已經(jīng)介紹了Bunch。
    target_name,label和filenames這幾個(gè)成員都是我們自己定義的玩意兒,前面已經(jīng)講過不再贅述。
    下面我們講一下tdm和vocabulary(這倆玩意兒也都是我們自己創(chuàng)建的):
    tdm存放的是計(jì)算后得到的TF-IDF權(quán)重矩陣。請記住,我們后面分類器需要的東西,其實(shí)就是訓(xùn)練集的tdm和標(biāo)簽label,因此這個(gè)成員是
    很重要的。
    vocabulary是詞向量空間的索引,例如,如果我們定義的詞向量空間是(我,喜歡,相國大人),那么vocabulary就是這樣一個(gè)索引字典
    vocabulary={"我":0,"喜歡":1,"相國大人":2},你可以簡單的理解為:vocabulary就是詞向量空間的坐標(biāo)軸,索引值相當(dāng)于表明了第幾
    個(gè)維度。
    我們現(xiàn)在就是要構(gòu)建一個(gè)詞向量空間,因此在初始時(shí)刻,這個(gè)tdm和vocabulary自然都是空的。如果你在這一步將vocabulary賦值了一個(gè)
    自定義的內(nèi)容,那么,你是傻逼。
    '''

    '''
    與下面這2行代碼等價(jià)的代碼是:
    vectorizer=CountVectorizer()#構(gòu)建一個(gè)計(jì)算詞頻(TF)的玩意兒,當(dāng)然這里面不只是可以做這些
    transformer=TfidfTransformer()#構(gòu)建一個(gè)計(jì)算TF-IDF的玩意兒
    tfidf=transformer.fit_transform(vectorizer.fit_transform(corpus))
    #vectorizer.fit_transform(corpus)將文本corpus輸入,得到詞頻矩陣
    #將這個(gè)矩陣作為輸入,用transformer.fit_transform(詞頻矩陣)得到TF-IDF權(quán)重矩陣

    看名字你也應(yīng)該知道:
    Tfidf-Transformer + Count-Vectorizer  = Tfidf-Vectorizer
    下面的代碼一步到位,把上面的兩個(gè)步驟一次性全部完成

    值得注意的是,CountVectorizer()和TfidfVectorizer()里面都有一個(gè)成員叫做vocabulary_(后面帶一個(gè)下劃線)
    這個(gè)成員的意義,與我們之前在構(gòu)建Bunch對象時(shí)提到的自己定義的那個(gè)vocabulary的意思是一樣的,相當(dāng)于詞向量
    空間的坐標(biāo)軸。顯然,我們在第45行中創(chuàng)建tfidfspace中定義的vocabulary就應(yīng)該被賦值為這個(gè)vocabulary_

    他倆還有一個(gè)叫做vocabulary(后面沒有下劃線)的參數(shù),這個(gè)參數(shù)和我們第45中講到的意思是一樣的。
    那么vocabulary_和vocabulary的區(qū)別是什么呢?
    vocabulary_:是CountVectorizer()和TfidfVectorizer()的內(nèi)部成員,表示最終得到的詞向量空間坐標(biāo)
    vocabulary:是創(chuàng)建CountVectorizer和TfidfVectorizer類對象時(shí),傳入的參數(shù),它是我們外部輸入的空間坐標(biāo),不寫的話,函數(shù)就從
    輸入文檔中自己構(gòu)造。
    一般情況它倆是相同的,不一般的情況沒遇到過。
    '''
    #構(gòu)建一個(gè)快樂地一步到位的玩意兒,專業(yè)一點(diǎn)兒叫做:使用TfidfVectorizer初始化向量空間模型
    #這里面有TF-IDF權(quán)重矩陣還有我們要的詞向量空間坐標(biāo)軸信息vocabulary_
    vectorizer = TfidfVectorizer(stop_words=stpwrdlst, sublinear_tf=True, max_df=0.5)
    '''
    關(guān)于參數(shù),你只需要了解這么幾個(gè)就可以了:
    stop_words:
    傳入停用詞,以后我們獲得vocabulary_的時(shí)候,就會(huì)根據(jù)文本信息去掉停用詞得到
    vocabulary:
    之前說過,不再解釋。
    sublinear_tf:
    計(jì)算tf值采用亞線性策略。比如,我們以前算tf是詞頻,現(xiàn)在用1+log(tf)來充當(dāng)詞頻。
    smooth_idf:
    計(jì)算idf的時(shí)候log(分子/分母)分母有可能是0,smooth_idf會(huì)采用log(分子/(1+分母))的方式解決。默認(rèn)已經(jīng)開啟,無需關(guān)心。
    norm:
    歸一化,我們計(jì)算TF-IDF的時(shí)候,是用TF*IDF,TF可以是歸一化的,也可以是沒有歸一化的,一般都是采用歸一化的方法,默認(rèn)開啟.
    max_df:
    有些詞,他們的文檔頻率太高了(一個(gè)詞如果每篇文檔都出現(xiàn),那還有必要用它來區(qū)分文本類別嗎?當(dāng)然不用了呀),所以,我們可以
    設(shè)定一個(gè)閾值,比如float類型0.5(取值范圍[0.0,1.0]),表示這個(gè)詞如果在整個(gè)數(shù)據(jù)集中超過50%的文本都出現(xiàn)了,那么我們也把它列
    為臨時(shí)停用詞。當(dāng)然你也可以設(shè)定為int型,例如max_df=10,表示這個(gè)詞如果在整個(gè)數(shù)據(jù)集中超過10的文本都出現(xiàn)了,那么我們也把它列
    為臨時(shí)停用詞。
    min_df:
    與max_df相反,雖然文檔頻率越低,似乎越能區(qū)分文本,可是如果太低,例如10000篇文本中只有1篇文本出現(xiàn)過這個(gè)詞,僅僅因?yàn)檫@1篇
    文本,就增加了詞向量空間的維度,太不劃算。
    當(dāng)然,max_df和min_df在給定vocabulary參數(shù)時(shí),就失效了。
    '''

    #此時(shí)tdm里面存儲(chǔ)的就是if-idf權(quán)值矩陣
    tfidfspace.tdm = vectorizer.fit_transform(bunch.contents)
    tfidfspace.vocabulary = vectorizer.vocabulary_

    _writebunchobj(space_path, tfidfspace)
    print "if-idf詞向量空間實(shí)例創(chuàng)建成功!?。?

if __name__ == '__main__':
    stopword_path = "train_word_bag/hlt_stop_words.txt"#停用詞表的路徑
    bunch_path = "train_word_bag/train_set.dat"  #導(dǎo)入訓(xùn)練集Bunch的路徑
    space_path = "train_word_bag/tfdifspace.dat"  # 詞向量空間保存路徑
    vector_space(stopword_path,bunch_path,space_path)




上面的代碼運(yùn)行之后,會(huì)將訓(xùn)練集數(shù)據(jù)轉(zhuǎn)換為TF-IDF詞向量空間中的實(shí)例,保存在train_word_bag/tfdifspace.dat中,具體來說,這個(gè)文件里面有兩個(gè)我們感興趣的東西,一個(gè)是vocabulary,即詞向量空間坐標(biāo),一個(gè)是tdm,即訓(xùn)練集的TF-IDF權(quán)重矩陣。

接下來,我們要開始第5步的操作,設(shè)計(jì)分類器,用訓(xùn)練集訓(xùn)練,用測試集測試。在做這些工作之前,你一定要記住,首先要把測試數(shù)據(jù)也映射到上面這個(gè)TF-IDF詞向量空間中,也就是說,測試集和訓(xùn)練集處在同一個(gè)詞向量空間(vocabulary相同),只不過測試集有自己的tdm,與訓(xùn)練集(train_word_bag/tfdifspace.dat)中的tdm不同而已。

同一個(gè)世界,同一個(gè)夢想。

至于說怎么弄,請看下節(jié)。

5,分類器

這里我們采用的是樸素貝葉斯分類器,今后我們會(huì)詳細(xì)講解它。

現(xiàn)在,你即便不知道這是個(gè)啥玩意兒,也一點(diǎn)不會(huì)影響你,這個(gè)分類器我們有封裝好了的函數(shù),MultinomialNB,這玩意兒獲取訓(xùn)練集的權(quán)重矩陣和標(biāo)簽,進(jìn)行訓(xùn)練,然后獲取測試集的權(quán)重矩陣,進(jìn)行預(yù)測(給出預(yù)測標(biāo)簽)。

下面我們開始動(dòng)手實(shí)踐吧!

首先,我們要把測試數(shù)據(jù)也映射到第4節(jié)中的那個(gè)TF-IDF詞向量空間上:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@version: python2.7.8 
@author: XiangguoSun
@contact: sunxiangguodut@qq.com
@file: test.py
@time: 2017/2/8 11:39
@software: PyCharm
"""
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
# 引入Bunch類
from sklearn.datasets.base import Bunch
import cPickle as pickle
from sklearn.feature_extraction.text import TfidfVectorizer

def _readfile(path):
    with open(path, "rb") as fp:
        content = fp.read()
    return content

def _readbunchobj(path):
    with open(path, "rb") as file_obj:
        bunch = pickle.load(file_obj)
    return bunch

def _writebunchobj(path, bunchobj):
    with open(path, "wb") as file_obj:
        pickle.dump(bunchobj, file_obj)

def vector_space(stopword_path,bunch_path,space_path,train_tfidf_path):

    stpwrdlst = _readfile(stopword_path).splitlines()
    bunch = _readbunchobj(bunch_path)
    tfidfspace = Bunch(target_name=bunch.target_name, label=bunch.label, filenames=bunch.filenames, tdm=[], vocabulary={})

    #導(dǎo)入訓(xùn)練集的TF-IDF詞向量空間
    trainbunch = _readbunchobj(train_tfidf_path)
    tfidfspace.vocabulary = trainbunch.vocabulary

    vectorizer = TfidfVectorizer(stop_words=stpwrdlst, sublinear_tf=True, max_df=0.5,vocabulary=trainbunch.vocabulary)
    tfidfspace.tdm = vectorizer.fit_transform(bunch.contents)
    _writebunchobj(space_path, tfidfspace)
    print "if-idf詞向量空間實(shí)例創(chuàng)建成功!??!"

if __name__ == '__main__':
    stopword_path = "train_word_bag/hlt_stop_words.txt"#停用詞表的路徑
    bunch_path = "test_word_bag/test_set.dat"   # 詞向量空間保存路徑
    space_path = "test_word_bag/testspace.dat"   # TF-IDF詞向量空間保存路徑
    train_tfidf_path="train_word_bag/tfdifspace.dat"
    vector_space(stopword_path,bunch_path,space_path,train_tfidf_path)


你已經(jīng)發(fā)現(xiàn)了,這段代碼與第4節(jié)幾乎一模一樣,唯一不同的就是在第39~41行中,我們導(dǎo)入了第4節(jié)中訓(xùn)練集的IF-IDF詞向量空間,并且第41行將訓(xùn)練集的vocabulary賦值給測試集的vocabulary,第43行增加了入口參數(shù)vocabulary,原因在上一節(jié)中都已經(jīng)說明,不再贅述。

考慮到第4節(jié)和剛才的代碼幾乎完全一樣,因此我們可以將這兩個(gè)代碼文件統(tǒng)一為一個(gè):

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@version: python2.7.8 
@author: XiangguoSun
@contact: sunxiangguodut@qq.com
@file: TFIDF_space.py
@time: 2017/2/8 11:39
@software: PyCharm
"""
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

from sklearn.datasets.base import Bunch
import cPickle as pickle
from sklearn.feature_extraction.text import TfidfVectorizer

def _readfile(path):
    with open(path, "rb") as fp:
        content = fp.read()
    return content

def _readbunchobj(path):
    with open(path, "rb") as file_obj:
        bunch = pickle.load(file_obj)
    return bunch

def _writebunchobj(path, bunchobj):
    with open(path, "wb") as file_obj:
        pickle.dump(bunchobj, file_obj)

def vector_space(stopword_path,bunch_path,space_path,train_tfidf_path=None):

    stpwrdlst = _readfile(stopword_path).splitlines()
    bunch = _readbunchobj(bunch_path)
    tfidfspace = Bunch(target_name=bunch.target_name, label=bunch.label, filenames=bunch.filenames, tdm=[], vocabulary={})

    if train_tfidf_path is not None:
        trainbunch = _readbunchobj(train_tfidf_path)
        tfidfspace.vocabulary = trainbunch.vocabulary
        vectorizer = TfidfVectorizer(stop_words=stpwrdlst, sublinear_tf=True, max_df=0.5,vocabulary=trainbunch.vocabulary)
        tfidfspace.tdm = vectorizer.fit_transform(bunch.contents)

    else:
        vectorizer = TfidfVectorizer(stop_words=stpwrdlst, sublinear_tf=True, max_df=0.5)
        tfidfspace.tdm = vectorizer.fit_transform(bunch.contents)
        tfidfspace.vocabulary = vectorizer.vocabulary_

    _writebunchobj(space_path, tfidfspace)
    print "if-idf詞向量空間實(shí)例創(chuàng)建成功!??!"

if __name__ == '__main__':

    stopword_path = "train_word_bag/hlt_stop_words.txt"
    bunch_path = "train_word_bag/train_set.dat"
    space_path = "train_word_bag/tfdifspace.dat"
    vector_space(stopword_path,bunch_path,space_path)

    bunch_path = "test_word_bag/test_set.dat"
    space_path = "test_word_bag/testspace.dat"
    train_tfidf_path="train_word_bag/tfdifspace.dat"
    vector_space(stopword_path,bunch_path,space_path,train_tfidf_path)


哇哦,你好棒!現(xiàn)在連注釋都不用,就可以看懂代碼了。。。

對測試集進(jìn)行了上述處理后,接下來的步驟,變得如此輕盈和優(yōu)雅。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@version: python2.7.8 
@author: XiangguoSun
@contact: sunxiangguodut@qq.com
@file: NBayes_Predict.py
@time: 2017/2/8 12:21
@software: PyCharm
"""
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

import cPickle as pickle
from sklearn.naive_bayes import MultinomialNB  # 導(dǎo)入多項(xiàng)式貝葉斯算法


# 讀取bunch對象
def _readbunchobj(path):
    with open(path, "rb") as file_obj:
        bunch = pickle.load(file_obj)
    return bunch

# 導(dǎo)入訓(xùn)練集
trainpath = "train_word_bag/tfdifspace.dat"
train_set = _readbunchobj(trainpath)

# 導(dǎo)入測試集
testpath = "test_word_bag/testspace.dat"
test_set = _readbunchobj(testpath)

# 訓(xùn)練分類器:輸入詞袋向量和分類標(biāo)簽,alpha:0.001 alpha越小,迭代次數(shù)越多,精度越高
clf = MultinomialNB(alpha=0.001).fit(train_set.tdm, train_set.label)

# 預(yù)測分類結(jié)果
predicted = clf.predict(test_set.tdm)

for flabel,file_name,expct_cate in zip(test_set.label,test_set.filenames,predicted):
    if flabel != expct_cate:
        print file_name,": 實(shí)際類別:",flabel," -->預(yù)測類別:",expct_cate

print "預(yù)測完畢!!!"

# 計(jì)算分類精度:
from sklearn import metrics
def metrics_result(actual, predict):
    print '精度:{0:.3f}'.format(metrics.precision_score(actual, predict,average='weighted'))
    print '召回:{0:0.3f}'.format(metrics.recall_score(actual, predict,average='weighted'))
    print 'f1-score:{0:.3f}'.format(metrics.f1_score(actual, predict,average='weighted'))

metrics_result(test_set.label, predicted)
出錯(cuò)的這個(gè),是我故意制造的,(因?yàn)閷?shí)際分類精度100%,不能很好的說明問題)

效果圖:



 

當(dāng)然,你也可以采用其他分類器,比如KNN

6,評價(jià)與小結(jié)

評價(jià)部分的實(shí)際操作我們已經(jīng)在上一節(jié)的代碼中給出了。這里主要是要解釋一下代碼的含義,以及相關(guān)的一些概念。

截止目前,我們已經(jīng)完成了全部的實(shí)踐工作。接下來,你或許希望做的是:

1,分詞工具和分詞算法的研究

2,文本分類算法的研究

這些內(nèi)容,博主會(huì)在今后的時(shí)間里,專門研究并寫出博文。

整個(gè)工程的完整源代碼到這里下載:

https://github.com/sunxiangguo/chinese_text_classification

需要說明的是,在工程代碼和本篇博文中,細(xì)心的你已經(jīng)發(fā)現(xiàn)了,我們所有的路徑前面都有一個(gè)點(diǎn)“. /”,這主要是因?yàn)槲覀儾恢滥鷷?huì)將工程建在哪個(gè)路徑內(nèi),因此這個(gè)表示的是你所在項(xiàng)目的目錄,本篇博文所有路徑都是相對路徑。因此你需要自己注意一下。工程里面語料庫是空的,因?yàn)樯蟼髻Y源受到容量的限制。你需要自己添加。

7,進(jìn)一步的討論

我們的這些工作究竟實(shí)不實(shí)用?這是很多人關(guān)心的問題。事實(shí)上,本博文的做法,是最經(jīng)典的文本分類思想。也是你進(jìn)一步深入研究文本分類的基礎(chǔ)。在實(shí)際工作中,用本文的方法,已經(jīng)足夠勝任各種情況了。


那么,我們也許想問,有沒有更好,更新的技術(shù)?答案是有的。未來,博主會(huì)集中介紹兩種技術(shù):
1.利用LDA模型進(jìn)行文本分類
2.利用深度學(xué)習(xí)進(jìn)行文本分類


利用深度學(xué)習(xí)進(jìn)行文本分類,要求你必須對深度學(xué)習(xí)的理論有足夠多的掌握。
為此,你可以參考博主的其他博文,
例如下面的這個(gè)系列博文《卷積神經(jīng)網(wǎng)絡(luò)CNN理論到實(shí)踐》。
這是一些列的博文。與網(wǎng)上其他介紹CNN的博文不同的是:
  1. 我們會(huì)全方位,足夠深入的為你講解CNN的知識(shí)。包括很多,你之前在網(wǎng)上找了很多資料也沒搞清楚的東西。
  2. 我們會(huì)利用CNN做文本分類的實(shí)踐。
  3. 我們會(huì)繪制大量精美的示意圖。保證博文的高質(zhì)量和美觀。




8,At last





welcome!

Xiangguo Sun 

sunxiangguodut@qq.com 

http://blog.csdn.net/github_36326955 



Welcome to my blog column: Dive into ML/DL!

(click here to blog column Dive into ML/DL)

這里寫圖片描述


I devote myself to dive into typical algorithms on machine learning and deep learning, especially the application in the area of computational personality.

My research interests include computational personality, user portrait, online social network, computational society, and ML/DL. In fact you can find the internal connection between these concepts: 

這里寫圖片描述

In this blog column, I will introduce some typical algorithms about machine learning and deep learning used in OSNs(Online Social Networks), which means we will include NLP, networks community, information diffusion,and individual recommendation systemApparently, our ultimate target is to dive into user portrait , especially the issues on your personality analysis.


All essays are created by myself, and copyright will be reserved by me. You can use them for non-commercical intention and if you are so kind to donate me, you can scan the QR code below. All donation will be used to the library of charity for children in Lhasa.



    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    最近的中文字幕一区二区| 日韩一区二区三区嘿嘿| 懂色一区二区三区四区| 激情亚洲内射一区二区三区| 一区二区福利在线视频| 久热人妻中文字幕一区二区| 经典欧美熟女激情综合网 | 国产欧美一区二区另类精品| 午夜福利大片亚洲一区| 一区二区三区四区亚洲专区| 最近的中文字幕一区二区| 夜夜嗨激情五月天精品| 欧美自拍偷自拍亚洲精品| 国产一区二区不卡在线播放 | 成人精品一区二区三区在线| 天海翼精品久久中文字幕| 国产剧情欧美日韩中文在线| 欧美日韩国产精品第五页| 91免费精品国自产拍偷拍| 久久人妻人人澡人人妻| 国产一区二区三中文字幕 | 又黄又硬又爽又色的视频 | 国产日韩欧美在线播放| 亚洲一级在线免费观看| 操白丝女孩在线观看免费高清| 国产精品一区二区三区欧美| 久久91精品国产亚洲| 日韩一区二区三区在线欧洲| 国产精品推荐在线一区| 丰满的人妻一区二区三区| 国产综合一区二区三区av| 欧美午夜国产在线观看| 日韩精品综合免费视频| 韩国日本欧美国产三级| 亚洲欧洲一区二区综合精品| 国产一区二区三中文字幕 | 日韩一级毛一欧美一级乱| 欧美日韩精品久久第一页| 日本免费一级黄色录像| 日韩三极片在线免费播放| 超碰在线播放国产精品|