• 技术文章 >头条

    Python分析倚天屠龙记,你get到了没?

     Ly Ly2020-06-13 16:40:05转载2268

    最近在了解到,在机器学习中,自然语言处理是较大的一个分支。存在许多挑战。例如: 如何分词,识别实体关系,实体间关系,关系网络展示等。

    我用Jieba + Word2vec + NetworkX 结合在一起,做了一次自然语言分析。语料是 倚天屠龙记。 之前也有很多人用金庸的武侠小说做分析和处理,希望带来一些不同的地方。截几张图来看看:

    所有人物的相似图连接。

    p1.jpg

    p2.jpg

    关系同上。展示形式为多中心结构

    p3.jpg

    以张无忌的不同身份为中心的网络关系图。

    这次分析的不一样之处主要是:

    1、Word2Vec的相似度结果 - 作为后期社交网络权重

    2、NetworkX中分析和展示

    上面两个方法结合起来,可以大幅减少日常工作中阅读文章的时间。 采用机器学习,可以从头到尾半自动抽取文章中的实体信息,节约大量时间和成本。 在各种工作中都有利用的场景, 如果感兴趣的朋友,可以联系合作。

    先来看看,用Word2Vec+NetworkX 可以发现什么。

    p4.jpg

    一、分析结果

    实体的不同属性(张无忌的总多马甲)

    张无忌,无忌,张教主,无忌哥哥,张公子。同一个张无忌有多个身份,不同身份又和不同的人联系,有不一样的相似度。

    先来看看图:

    p5.jpg

    无忌哥哥是过于亲密的名字,一般不喊。好似和这个词相似度高的都是比较奇怪的角色。

    无忌是关系熟了以后,平辈或者长辈可以称呼的名字。还有周姑娘,殷姑娘等

    张无忌是通用的名字,人人可以称呼 和马甲联系密切。

    张公子是礼貌尊称。 例如,黄衫女子,汝阳王等

    张教主是头衔。既要尊重,也表示其实不太熟,有时还有些敌意。 例如: 朱元璋

    注:

    1、图是Networkx 基于Word2vex画出来了,上面的描述是我的人工分析。

    2、赵敏不在上面的网络关系图中。Word2Vec计算出来 张无忌和赵敏 相似度不太高。有些出乎我的意料。 仔细回忆一下,当年看此书时,突然就发现二人在一起了,显得比较突兀。推想起来,书中世界二人成婚了,如果变成现实世界,二人关系比较悬。

    p6.jpg

    二、实现过程

    主要步骤:

    准备语料

    倚天屠龙记 小说的文本文件

    自定义分词词典 (小说中的人物名,网上有现成的,约180个)

    停用词表

    准备工具

    Python Pandas, Numpy,Scipy(标准库)

    Jieba(中文分词)

    Word2vec (单词向量化工具,可以计算单词之间的详细度)

    Networks(网络图工具,用于展示复杂的网络关系

    数据预处理

    文本文件转发成utf8(pandas)

    文本文件分句,分词(Jieba)

    文本文件分句,分词, 分析词性,主要是人名(Jieba)

    更新自定义词典,重新分词(整个过程需要几遍,直至满意)

    手工少量删除(分词出来的人名误判率不高,但是还是存在一些。例如:赵敏笑道,可以被识别的 一个叫 赵敏笑的人。 这部分工作还需要手工做。 除非有更好的分词工具,或者可以训练的分词工具,才能解决这一问题。

    Word2Vec 训练模型。这个模型可以计算两个人之间的相似度

    采用300个维度

    过滤词频小于20

    滑动窗口 为20

    下采样:0.001

    生成实体关系矩阵。

    网上没找找到现成库,我就自己写了一个。

    N*N 维度。 N是人名数量。

    用上面WordVec的模型来,填充实体关系矩阵

    NetworkX 生成网络图

    节点是人名

    边是两个节点之间的线条。也就是两个人之间的关系。

    三、部分代码实现

    初始化

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    import

     numpy

    as

     np

    import

     pandas

    as

     pd

    import

     jieba

    import

     jieba.posseg

    as

     posseg

    %matplotlib

    inline

    数据分词,清洗

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    78

    79

    80

    81

    82

    83

    84

    85

    86

    87

    88

    89

    90

    91

    92

    93

    94

    95

    96

    97

    98

    99

    100

    101

    102

    103

    104

    105

    106

    107

    108

    109

    110

    111

    112

    113

    114

    115

    116

    117

    118

    119

    120

    121

    122

    123

    124

    125

    126

    127

    128

    129

    130

    131

    132

    133

    134

    135

    136

    137

    138

    139

    140

    141

    142

    143

    144

    145

    146

    147

    148

    149

    150

    151

    152

    153

    154

    155

    156

    157

    158

    159

    160

    161

    162

    163

    164

    165

    166

    167

    168

    169

    170

    171

    172

    173

    174

    175

    176

    177

    178

    179

    180

    181

    182

    183

    184

    185

    186

    187

    188

    189

    190

    renming_file =

    "yttlj_renming.csv"

    jieba.load_userdict(renming_file)

    stop_words_file =

    "stopwordshagongdakuozhan.txt"

    stop_words = pd.read_csv(stop_words_file,header=

    None

    ,quoting=

    3

    ,sep=

    "\t"

    )[

    0

    ].values

    corpus =

    "yttlj.txt"

    yttlj = pd.read_csv(corpus,encoding=

    "gb18030"

    ,header=

    None

    ,names=[

    "sentence"

    ])

    def

     cut_join(s):

        new_s=list(jieba.cut(s,cut_all=

    False

    ))

    #分词

         

    #print(list(new_s))

        stop_words_extra =

    set

    ([

    ""

    ])

         

    for

     seg

    in

     new_s:

             

    if

     len(seg)==

    1

    :

                 

    #print("aa",seg)

                stop_words_extra.add(seg)

         

    #print(stop_words_extra)

         

    #print(len(set(stop_words)| stop_words_extra))

        new_s =

    set

    (new_s) -

    set

    (stop_words)-stop_words_extra

         

    #过滤标点符号

         

    #过滤停用词

        result =

    ","

    .join(new_s)

         

    return

      result

    def

     extract_name(s):

        new_s=posseg.cut(s)

    #取词性

        words=[]

        flags=[]

         

    for

     k,v

    in

     new_s:

             

    if

     len(k)>

    1

    :

                words.append(k)

                flags.append(v)

        full_wf[

    "word"

    ].extend(words)

        full_wf[

    "flag"

    ].extend(flags)

         

    return

     len(words)

    def

     check_nshow(x):

        nshow = yttlj[

    "sentence"

    ].str.count(x).sum()

         

    #print(x, nshow)

         

    return

     nshow

    # extract name & filter times

    full_wf={

    "word"

    :[],

    "flag"

    :[]}

    possible_name = yttlj[

    "sentence"

    ].apply(extract_name)

    #tmp_w,tmp_f

    df_wf = pd.

    DataFrame

    (full_wf)

    df_wf_renming = df_wf[(df_wf.flag==

    "nr"

    )].drop_duplicates()

    df_wf_renming.to_csv(

    "tmp_renming.csv"

    ,index=

    False

    )

    df_wf_renming = pd.read_csv(

    "tmp_renming.csv"

    )

    df_wf_renming.head()

    df_wf_renming[

    "nshow"

    ] = df_wf_renming.word.apply(check_nshow)

    df_wf_renming[df_wf_renming.nshow>

    20

    ].to_csv(

    "tmp_filtered_renming.csv"

    ,index=

    False

    )

    df_wf_renming[df_wf_renming.nshow>

    20

    ].shape

    #手工编辑,删除少量非人名,分词错的人名

    df_wf_renming=pd.read_csv(

    "tmp_filtered_renming.csv"

    )

    my_renming = df_wf_renming.word.tolist()

    external_renming = pd.read_csv(renming_file,header=

    None

    )[

    0

    ].tolist()

    combined_renming =

    set

    (my_renming) |

    set

    (external_renming)

    pd.

    DataFrame

    (list(combined_renming)).to_csv(

    "combined_renming.csv"

    ,header=

    None

    ,index=

    False

    )

    combined_renming_file =

    "combined_renming.csv"

    jieba.load_userdict(combined_renming_file)

    # tokening

    yttlj[

    "token"

    ]=yttlj[

    "sentence"

    ].apply(cut_join)

    yttlj[

    "token"

    ].to_csv(

    "tmp_yttlj.csv"

    ,header=

    False

    ,index=

    False

    )

    sentences = yttlj[

    "token"

    ].str.split(

    ","

    ).tolist()

    Word2Vec 向量化训练

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    # Set values for various parameters

    num_features =

    300

         

    # Word vector dimensionality                     

    min_word_count =

    20

        

    # Minimum word count                       

    num_workers =

    4

            

    # Number of threads to run in parallel

    context =

    20

               

    # Context window size                                                                                   

    downsampling =

    1e-3

        

    # Downsample setting for frequent words

    # Initialize and train the model (this will take some time)

    from

     gensim.models

    import

     word2vec

    model_file_name =

    'yttlj_model.txt'

      

    #sentences = w2v.LineSentence('cut_jttlj.csv')

    model = word2vec.

    Word2Vec

    (sentences, workers=num_workers, \

                size=num_features, min_count = min_word_count, \

                window = context, \

                sample = downsampling

                )

    model.save(model_file_name)

    建立实体关系矩阵

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    78

    79

    80

    81

    82

    83

    84

    85

    86

    87

    88

    89

    90

    91

    92

    93

    94

    95

    96

    97

    98

    99

    100

    101

    102

    103

    104

    105

    106

    107

    108

    109

    110

    111

    112

    113

    114

    115

    116

    117

    118

    119

    120

    entity = pd.read_csv(combined_renming_file,header=

    None

    ,index_col=

    None

    )

    entity = entity.rename(columns={

    0

    :

    "Name"

    })

    entity = entity.set_index([

    "Name"

    ],drop=

    False

    )

    ER = pd.

    DataFrame

    (np.zeros((entity.shape[

    0

    ],entity.shape[

    0

    ]),dtype=np.float32),index=entity[

    "Name"

    ],columns=entity[

    "Name"

    ])

    ER[

    "tmp"

    ] = entity.

    Name

    def

     check_nshow(x):

        nshow = yttlj[

    "sentence"

    ].str.count(x).sum()

         

    #print(x, nshow)

         

    return

     nshow

    ER[

    "nshow"

    ]=ER[

    "tmp"

    ].apply(check_nshow)

    ER = ER.drop([

    "tmp"

    ],axis=

    1

    )

    count =

    0

    for

     i

    in

     entity[

    "Name"

    ].tolist():

        count +=

    1

         

    if

     count % round(entity.shape[

    0

    ]/

    10

    ) ==

    0

    :

             

    print

    (

    "{0:.1f}% relationship has been checked"

    .format(

    100

    *count/entity.shape[

    0

    ]))

         

    elif

     count == entity.shape[

    0

    ]:

             

    print

    (

    "{0:.1f}% relationship has been checked"

    .format(

    100

    *count/entity.shape[

    0

    ]))

         

    for

     j

    in

     entity[

    "Name"

    ]:

            relation =

    0

             

    try

    :

                relation = model.wv.similarity(i,j)

                ER.loc[i,j] = relation

                 

    if

     i!=j:

                    ER.loc[j,i] = relation

             

    except

    :

                relation =

    0

    ER.to_hdf(

    "ER.h5"

    ,

    "ER"

    )

    NetworkX 展示人物关系图

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    import

     networkx

    as

     nx

    import

     matplotlib.pyplot

    as

     plt

    import

     pandas

    as

     pd

    import

     numpy

    as

     np

    import

     pygraphviz

    from

     networkx.drawing.nx_agraph

    import

     graphviz_layout

    专题推荐:python
    上一篇:Python中的垃圾回收机制!惊艳了! 下一篇:世界上人们最喜欢的数据库和最喜欢的语言Python结合会发生什么?

    相关文章推荐

    • 怎样查看python环境是否安装正确• python倒排列是什么意思• python的运行文件在哪个文件夹• python3中怎么调用不同包中的方法• python的dtype可用对象有哪些?• python定义int型变量吗?

    全部评论我要评论

    © 2021 Python学习网 苏ICP备2021003149号-1

  • 取消发布评论
  • 

    Python学习网