• NLP_learning 导论:自然语言处理概述+基本文本处理操作



    一、自然语言处理概述

    定义:自然语言处理(Natural Language Processing)简称NLP,是一种利用计算机为工具对人类特有的书面形式和口语形式的自然语言的信息进行各种类型处理和加工的技术。

    任务:通过处理和理解语言,来构建执行某些任务的系统

    地位:人工智能与语言学的交叉学科,是人工智能的一个重要分支

    NLP核心问题:文本分类、关键词提取、情感分析、语义消歧、主题模型、机器翻译、问题回答、汉语分词、垂直领域的对话机器人

    ·序列标注:分词、pos tag、NER、语义标注
    ·分类任务:文本分类、情感计算
    ·句子关系判断:QA、自然语言推理
    ·生成式任务:机器翻译、文本摘要

    NLP典型应用:搜索引擎、文本主题与标签分类、文本创作与生成、机器翻译、情感分析、舆情监控、语音识别系统、对话机器人

    二、基本文本处理操作

    1、清理与替换

    去掉空格

    en_str = " hello world, hello, my name is Lucky_star!  "
    #去空格及特殊符号
    a = en_str.strip().lstrip().rstrip(',')
    #strip能够把句子前后的空格去掉,lstrip左侧空格去除,rstrip右侧空格去除
    print(a)
    #hello world, hello, my name is Lucky_star!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    替换字符串

    #字符串替换
    b = en_str.replace('hello', 'hi')
    #将hello替换成hi,以返回值的形式替换,所以原字符串并没有发生变化
    print(b)
    # hi world, hi, my name is Lucky_star!
    
    • 1
    • 2
    • 3
    • 4
    • 5

    小运用

    zh_str = "大家好, 我叫陆超"
    zh_str = zh_str.strip().lstrip().rstrip(',')
    zh_str = zh_str.replace("陆超", "小星星")
    print(zh_str)
    #大家好, 我叫小星星
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、截取

    my_str = "大家好,我是李雪琴,我在北京大学,你吃饭没呢?"
    #从左到右index从0开始,可以用index进行切片(左闭右开)
    print(my_str[0:3])
    #大家好
    
    #从左往右index从0开始,可以用index进行切片(左闭右开)
    print(my_str[4: 4+5])
    #我是李雪琴
    
    #从右往左index从-1开始
    print(my_str[-1-5: -1])  #同样是左闭右开
    #你吃饭没呢
    #间隔截取
    print(my_str[: : 2])
    #大好我李琴我北大,吃没?
    
    #翻转
    print(my_str[::-1])
    #?呢没饭吃你,学大京北在我,琴雪李是我,好家大
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3、连接与分割

    str1 = "大家好,我是lucky_star,有你们真好"
    str2 = "大家好,我是李雪琴,你吃饭没呢"
    print(str1 + str2)
    
    #通过join的方式连接
    strs = ["我是陆超", "我是李雪琴", "我是lucky_star"]
    print(";".join(strs))
    #我是陆超;我是李雪琴;我是lucky_star
    
    #通过split的方式切分
    tmp_str = "我是陆超;我是李雪琴;我是lucky_star"
    print(tmp_str.split(";"))
    #['我是陆超', '我是李雪琴', '我是lucky_star']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4、比较与排序

    en_strs = ['ABc', 'aCD', 'CdE', 'xYz']
    #以字母排序,注意是以返回值形态返回排序结果,不改变原来的list
    print(sorted(en_strs))
    #['ABc', 'CdE', 'aCD', 'xYz']
    
    #自定义排序方式
    def sort_fun(x):
        return x[1].lower()
    
    print(sorted(en_strs, key=sort_fun))
    #key输入sort_fun之后,就是按照函数返回结果进行排序,sort_fun是对将列表之后每个字符串中索引为1的字母变为小写之后进行排序
    #['ABc', 'aCD', 'CdE', 'xYz']
    
    print(sorted(en_strs, key=lambda x:x[2].lower()))
    #['ABc', 'aCD', 'CdE', 'xYz']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    5、查找与包含

    #查找可以用index和find
    zh_str = "我是陆超;我是李雪琴;我是毛毛姐"
    print(zh_str.index("陆超"))
    #2
    print(zh_str.index("毛毛姐"))
    #13
    
    #print(zh_str.index("来了,老弟"))
    #ValueError: substring not found
    
    print(zh_str.find("毛毛姐"))
    #13
    
    print(zh_str.find("来了,老弟"))
    #-1,因为找不到子串,所以索引返回为-1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    6、大小写变换

    en_str = "hello, my name is Patrick"
    print(en_str.lower())
    #将所有的字母小写化
    
    print(en_str.upper())
    #HELLO, MY NAME IS PATRICK
    
    print(en_str.capitalize()) #首字母大写化
    #Hello, my name is patrick
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    7、搜索查找更多的字符串相关操作

    print(help(str))

    三、python正则表达式

    正则表达式是处理字符串的强大工具,拥有独特的语法和独立的处理引擎。

    我们在大文本中匹配字符串时,有些情况用str自带的函数(index、find、in)就可以完成,有些情况会稍微复杂一些,比如说要查找所有“像邮箱”的字符串,所有和 xuniuedu/netease相关的句子),这个时候我们就需要像正则表达式这样具有某种模式的工具了。

    自然语言处理汇总的各种模型和算法要发挥作用就离不开“干净”的数据,而现实生活中的数据形态和干净程度不一,我们经常要做一些数据清洗和信息抽取的工作,这个时候正则表达式就可以发挥其强大的匹配作用了。正则表达是一套引擎,并不是python语言独有的功能或工具库。

    1、学习与验证工具

    在线验证工具:http://regexr.com/ 可以在线的方式学习与验证正则表达式的对错,左边还有对应的工具和速查表。

    正则表达式的语法
    请添加图片描述

    \w{2, 4}的含义是匹配常用字符串[A-Za-z0-9_]时,先从4个字符开始匹配,4个字符匹配不上,就匹配三个字符,三个字符匹配不上就匹配两个字符。
    请添加图片描述
    \w* 其中,*表示0个或者更多,最差的情况是不匹配,但凡能匹配到则会贪婪式得匹配下去。
    \w+, +表示一个或者更多个,至少要出现一个。如果中间有空格的话就匹配不上,一定不能为空。

    下面的例子可以展示出来,我们当正则表达规则为[RegExr *was]时,则表示RegExr与was之间有零个空格也可以匹配得上,[RegExr +was]则表示,至少要有1个空格才能匹配得上。注:在两个规则中,符号前面都有空格。
    请添加图片描述
    请添加图片描述

    请添加图片描述
    例如:在中括号中输入abc,意味着只要出现这三个字母中的一个字母就要选中
    或者是给一个范围[a-z],就能够选取a-z所有的字符串了。
    请添加图片描述

    请添加图片描述

    2、正则表达式的进阶练习

    https://alf.nu/RegexGolf

    答案在下方链接中
    https://blog.csdn.net/weixin_47532216/article/details/122615843

    3、python通过re模块提供对正则表达式的支持

    使用re的一般步骤:
    (1)将正则表达式的字符串形式破译为pattern实例;
    (2)将pattern实例处理文本并获得匹配的结果;
    (3)使用match实例获得信息,进行其他操作

    import re
    
    #1、将正则表达式的字符串形式编译为pattern实例
    pattern = re.compile(r'hello.*\!') #r = raw
    #.表示在hello之后匹配任意除换行符"\n"外的字符
    #*表示零次或者更多次
    #\!——>直到出现!
    
    #2、使用pattern匹配文本,获得匹配结果,无法匹配将返回None
    match = pattern.match('hello, what is your name! How are you')
    #一定要注意的是match这个函数一定是从头开始匹配,在hello之前如果有其他的词是匹配不上的
    #3、使用match实例获得信息,进行其他操作
    if match:
        print(match.group())
    #hello, what is your name!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    re.compile(strPattern[, flag]):
    用于将字符串形式的正则表达式编译为pattern对象
    第二个flag参数是匹配模式,取值可以使用按位或者运算符’|’表示同时生效,比如re.compile(‘pattern’,re.I|re.M)等价于re.compile(‘(?im)pattern’)

    >>> p=re.compile("\w+",re.I|re.M)
    >>> p.match("sadf234").group()
    'sadf234'
    >>> p=re.compile("(?im)\w+")
    >>> p.match("sadf234").group()
    'sadf234'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    compile

    flag可选的值有:
    re.I(re.IGNORECASE) :使匹配对大小写不敏感
    re.L(re.LOCAL) :做本地化识别(locale-aware)匹配
    re.M(re.MULTILINE) :多行匹配,影响 ^ 和 $
    re.S(re.DOTALL) :使 . 匹配包括换行在内的所有字符
    re.U(re.UNICODE):根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
    re.X(re.VERBOSE):该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解

    regex_1 = re.compile(r'''\d + #数字部分
                             \.   #小数点部分
                             \d * #小数的数字部分''', re.X)
    
    regex_2 = re.compile(r"\d+\.\d*")
    
    • 1
    • 2
    • 3
    • 4
    • 5

    match

    Match对象是一次匹配的结果,包含了很多关于次匹配的信息,可以使用match提供的可读属性或方法来获取这些信息。

    match属性:
    (1)string:匹配时使用的文本
    (2)re:匹配时使用的Pattern对象
    (3)pos:文本中正则表达式开始搜索的索引
    (4)endpos:文本中正则表达式搜索结束的索引
    (5)lastindex:最后一个被捕获的分组在文本中的索引
    (6)lastgroup:最后一个被捕获的分组的别名。

    match方法:
    (1)group([group1,…])
    获得一个或多个分组截获的字符串;指定多个参数时将以远足形式返回。group1 可以使用编号也可以使用别名;编号0代表整个匹配的子串;不填写参数时,返回group(0);没有截获字符串的组返回None;截获了多次的组返回最后一次截获的字符串;
    (2)group([default])
    以元素形式返回全部分组截获的字符串。相当于调用了group(1, 2, …,last)。default表示没有截获字符串的组以这个值替代,默认None;
    (3)grouppridict([default])
    返回已有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。default含义同上。
    (4)start([group])
    返回指定的组截获的子串在string中的起始索引(子串的第一个字符的索引)。group默认值为0;
    (5)end([group])
    返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引),group默认为0
    (6)span([group])
    返回(start([group]), end([group]))
    (7)expand(template)
    将匹配到的分组带入到template中然后返回。template中可以用\id或\g引用分组,但是不能使用编号0,\id和\g是等价的;但\10将被认为是第10个分组,如果想表达的是\1之后为字符‘0’,只能用\g<1>0

    m = re.match(r'(\w+) (\w+)(?P.*)', 'hello Luckystar!')
    #截获三个组,第一个组是从第一个字符串开始,捕获所有出现次数不少于1的字符串,空格之后开始捕获第二个分组(与第一个分组同理)
    #第三个分组捕获的是标点符号
    
    
    print("m.string:", m.string)
    #m.string: hello Luckystar!
    print("m.re:", m.re)
    #m.re: re.compile('(\\w+) (\\w+)(?P.*)')
    print("m.pos:", m.pos)
    #m.pos: 0
    print("m.endpos:", m.endpos)
    #m.endpos: 16
    print("m.lastindex:", m.lastindex)
    #m.lastindex: 3
    print("m.lastgroup:", m.lastgroup)
    #m.lastgroup: sign
    
    print("m.group(1, 2):", m.group(1, 2))
    #m.group(1, 2): ('hello', 'Luckystar')
    print("m.groups():", m.groups())
    ##('hello', 'Luckystar', '!')
    print("m.groupdict():", m.groupdict())
    #m.groupdict(): {'sign': '!'}
    print("m.start(2)", m.start(2))
    #m.start(2) 6
    print("m.end(2)", m.end(2))
    #m.end(2) 15
    print("m.span(2)", m.span(2))
    #m.span(2) (6, 15)
    print(r"m.expand(r'\2 \1\3'):", m.expand(r'\2 \1\3'))
    #m.expand(r'\2 \1\3'): Luckystar hello!
    
    • 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

    pattern.match和re.match在某些场景下可以替换使用:

    pattern = re.compile(r'hello')
    pattern.match('hello world!')
    
    #上面两句代码等同于:
    re.match(r”hello”,”hello world!”)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    pattern.match和re.match的区别:
    <1>对于pattern.match来说,pattern实例化一次,可以进行多次使用

    pattern = re.compile(r'hello')
    match_1 = pattern.match("hello word!")
    match_2 = pattern.match("hello lucky_star")
    print(match_1.group())
    #hello
    print(match_2.group())
    #hello
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    <2>pattern.match和re.match的主要区别是pattern.match可以指定匹配的起始位置,而re.match()不能指定匹配的区间pos和endpos两个参数。

    pattern = re.compile("\w+")
    m = pattern.match("qwer123", 0, 2).group()
    m_ = pattern.match("qwer123", 0, 3).group()
    print(m)
    #qw
    print(m_)
    #qwe
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    pattern

    pattern对象是一个编译好的正则表达式,通过pattern提供的一系列方法可以对文本进行匹配查找;
    pattern不能直接实例化,必须使用re.compile()进行构造;
    pattern提供了几个可读属性用于获取表达式的相关信息:
    (1)pattern:编译时用的表达式字符串
    (2)flags:编译时用的匹配模式,数字形式
    (3)groups:表达式中分组的数量;
    (4)groupindex:以表达式中有别名的组的别名为键,以该组对应的编号为值的字典,没有别名的组不包含在内

    p = re.compile(r'(\w+) (\w+)(?P).*', re.DOTALL)
    print("p.pattern:", p.pattern)
    #p.pattern: (\w+) (\w+)(?P).*
    print("p.flags:", p.flags) #匹配的模式
    #p.flags: 48
    print("p.groups:", p.groups) #分组的数量
    #p.groups: 3
    print("p.groupindex:", p.groupindex)
    #p.groupindex: {'sign': 3}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    pattern.match()方法:

    这个方法将在字符串string的pos位置开始尝试匹配pattern(pattern就是通过re.compile()方法编译后返回的对象),如果pattern匹配成功,无论是否达到结束位置endpos,都会返回一个匹配成功后的Match对象;如果匹配不成功,或者pattern未匹配结束就达到endpos,则返回None。

    参数说明:
    string:被匹配的字符串
    pos:匹配的起始位置,可选,默认为0
    endpos:匹配的结束位置,可选,默认为len(string)

    匹配到的Match对象,我们将使用其具有的group()方法取出匹配结果。

    pattern = re.compile("\w+")
    m = pattern.match("qwer123", 0, 2).group()
    m_ = pattern.match("qwer123", 0, 3).group()
    print(m)
    #qw
    print(m_)
    #qwe
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    match(string[, pos[, endpos]])等同于re.match(pattern, string[, flag])

    p = re.compile(r'(\w+) (\w+)(?P).*', re.DOTALL)
    match = p.match('hello Luckystar!')
    
    print("m.group(1, 2):", m.group(1, 2))
    #m.group(1, 2): ('hello', 'Luckystar')
    print("m.groups():", m.groups())
    ##('hello', 'Luckystar', '!')
    print("m.groupdict():", m.groupdict())
    #m.groupdict(): {'sign': '!'}
    print("m.start(2)", m.start(2))
    #m.start(2) 6
    print("m.end(2)", m.end(2))
    #m.end(2) 15
    print("m.span(2)", m.span(2))
    #m.span(2) (6, 15)
    print(r"m.expand(r'\2 \1\3'):", m.expand(r'\2 \1\3'))
    #m.expand(r'\2 \1\3'): Luckystar hello!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    pattern. search()方法

    该方法的作用是在string[pos, endpos]区间从pos下标处开始匹配pattern,如果匹配成功,返回匹配成功的Match对象;如果没有匹配成功,则将pos加1后重新尝试匹配,直到pos=endpos时仍无法匹配则返回None。

    参数说明:
    string:被匹配的字符串
    pos:匹配的起始位置,可选,默认为0
    endpos:匹配的结束位置,可选,默认为len(string)
    也就是说如果不指定pos和endpos这两个参数的话,该方法会扫描整个字符串

    pattern = re.compile("\d+\w*")
    m_ = pattern.search('12abc123ABc123', 0, 10).group()
    m_1 = pattern.search('12abc123ABc123', 0, 9).group()
    print(m_)
    #12abc123AB
    print(m_1)
    #12abc123A
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    但是,pattern.match()和pattern.research()有区别,在不指定pos开始位置的索引时,match要从最开始的字符开始匹配,而research匹配的是子串,只要找到相应的字符即可开始匹配。

    pattern = re.compile(r'H.*g')
    match = pattern.search('hello Hanxiaoyang')
    m = pattern.match('hello Hanxiaoyang')
    
    if match:
        print(match.group())
    #Hanxiaoyang
    
    if m:
        print(m.group())
    #用match匹配不上
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    split

    split(string[, maxsplit])| re.split(pattern, string[, maxsplit]]):
    按照能够匹配的子串将string分割后再返回列表;
    maxsplit用于指定最大分割次数,不指定将全部分割

    p = re.compile(r'\d+')
    #\d+的意思是捕获数字,+的意思是,数字出现次数至少为1次
    print(p.split('one1two2three3four4'))
    #['one', 'two', 'three', 'four', '']
    
    • 1
    • 2
    • 3
    • 4

    findall

    findall(string[, pos[, endpos]]) | re.findall(pattern, string[,flag])
    搜索string,以列表形式返回全部能匹配到的子串

    p = re.compile(r'\d+')
    print(p.findall('one1two2three3four4'))
    #['1', '2', '3', '4']
    
    • 1
    • 2
    • 3

    finditer

    finditer(string[, pos[, endpos]]) | re.finditer(pattern, string[,flag])
    搜索string,返回一个顺序访问每一个匹配结果(match对象)的迭代器

    p = re.compile(r'\d+')
    
    for m in p.finditer('one1two2three3four4'):
        print(m.group())
    '''
    1
    2
    3
    4
    '''
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    sub

    sub(repl,string[,count]) | re.sub(pattern, repl, string[, count])

    p = re.compile(r'(\w+) (\w+)')
    s = 'i say, hello world'
    
    print(p.sub(r'\2 \1', s))
    #say i, world hello
    
    def func(m):
        return m.group(1).title() + ' ' + m.group(2).title()
    
    print(p.sub(func, s))
    #I Say, Hello World
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    subn

    返回sub(repl,string[,count]) ,替换的次数

    p = re.compile(r'(\w+) (\w+)')
    s = 'i say, hello world'
    
    print(p.subn(r'\2 \1', s))
    #('say i, world hello', 2)
    
    def func(m):
        return m.group(1).title() + ' ' + m.group(2).title()
    
    print(p.subn(func, s))
    #('I Say, Hello World', 2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    补充学习资料https://www.runoob.com/regexp/regexp-syntax.html

  • 相关阅读:
    10倍加速LLM计算效率:消失的矩阵乘
    QT+OSG/osgEarth编译之二十二:librttopo+Qt编译(一套代码、一套框架,跨平台编译,版本:librttopo-1.1.0)
    vue2(vuecli5)+预渲染插件prerender-spa-plugin-next解决seo问题
    netcat详细使用
    libstdc++.so.6: cannot open shared object file: No such file or directory
    小谈设计模式(2)—简单工厂模式
    Linux之gdb调试工具
    Docker---cgroups资源限制
    前端HTML5 +CSS3 5.CSS布局 5 定位
    Java 操作 Excel:生成数据、设置单元格样式、设置数据有效性(hutool)
  • 原文地址:https://blog.csdn.net/weixin_51589123/article/details/128068275