情感分析_情感分析模型 - CSDN
精华内容
参与话题
  • Pytorch+Google BERT模型(RoBERTa+LSTM+GRU)实战 BERT(Bidirectional Encoder Representations from Transformers)模型的前置基础知识,读者可以参阅以下的文章: Pytorch使用Google BERT模型进行中文文本分类...

    Pytorch+Google BERT模型(RoBERTa+LSTM+GRU)实战

    BERT(Bidirectional Encoder Representations from Transformers)模型的前置基础知识,读者可以参阅以下的文章:

    本案例实现RoBERTa+LSTM+GRU模型,根据互联网新闻文本标题及内容,判断新闻的情感极性(正面、中性、负面),对新闻文本进行分类。

     

    1.输入数据源

    数据源是互联网新闻情感分析文件,每行记录包括序号,id,标题,内容,标签等内容,字段之间采用“,”进行分隔。

    ,id,title,content,label
    7139,a0c339acd9fa4ec0afe011a931551944,未来一周我市天气将持续阴雨,作者: 来源:吉安晚报 字数:338本报讯 记者报道:昨日,记者从市气象局获悉,未来一周,我市依然维持阴雨天气,气温偏低。    15日我市降水较弱,部分小雨,8—10℃;16日受冷空气影响,气温下降,阴有小雨,6—8℃;17—18日降水显著加强,部分地方有大雨,17日阴有小雨转中雨,3—6℃,18日中雨,部分大雨,3—6℃;19日降水停歇,气温回升。19日阴天多云,5—12℃;20日阴天多云,8—12℃;21日阴转小雨,9—12℃。    气象部门提醒市民,17—18日降雨较明显,道路湿滑、能见度低,各地需防范地质灾害的发生,田间湿度大的油菜田块,应及时清理厢沟、腰沟和围沟,以利降湿排渍;对于幼禽幼畜等养殖棚舍应做好温湿调控及疫病防御工作。\n特别声明:以上文章内容仅代表作者本人观点,不代表新浪看点观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与新浪看点联系。,1
    7145,6b06abb70944435089baa7a554d1bfae,【荐读】明天要开始降温了,最低温度只有10℃...,今天是2018年5月1日——国际劳动节,小编在这里祝大家节日快乐~向所有辛勤劳动的人们致敬!在外出游的小伙伴准备返程了吗?制造了前夜那场倾盆大雨,昨天雨水君有所收敛,上午阳光也挤出云层打了声招呼,总体白天天气还可以,适合出门“浪”,但昨天下午到夜里强对流云团又来袭,不过此次降水局地性较强,主要落区位于兰溪和武义,累积最大降水量位于兰溪厚仁达36.7mm。今晨天空依旧阴沉,上午阳光微透01气温的话,市区从22.4℃出发,上午11时已来到了28℃,预计今天白天最高冲刺30℃线——气温高+湿度大+风较弱=闷热闷热闷热!所以,大家在穿着方面要注意宽松透气~02雨水的话,今日依旧不打烊,今天中午到傍晚多云到阴,部分地区有阵雨或雷雨,有雷雨地区可伴有短时强降水、强雷电和局地6~8级雷雨大风等强天气;夜里阴有阵雨或雷雨,雨量中等;明天白天阴有阵雨,夜里雨渐止转阴到多云;后天多云到晴。因此,大家返程要趁早,出行注意交通安全~03此外,短期气温有所回落,明天温度稍低,21~26℃,而后天气温下降较明显16~24℃,4日早晨最低气温降至13℃左右,相比假期的闷热,体感会舒适不少。但仍需注意及时添衣,谨防感冒!一周天气5月总是大跨步向夏天迈进~整体气温趋势将在波动中继续上升,大家~准备好和春天说再见了吗?准备好和夏天重逢了吗?预计未来七天西南暖湿气流活跃,多降水天气,其中5~7日有一次较明显的雷阵雨天气过程。具体分日预报:05月02日(星期三):阴有阵雨,夜里雨渐止转阴到多云,温度20~26℃;05月03日(星期四):多云到晴,温度15~23℃;05月04日(星期五):晴转多云,夜里转阴有阵雨,温度13~25℃;05月05日(星期六):阴有阵雨或雷雨,温度17~23℃;05月06日(星期日):阴有时有阵雨或雷雨,温度19~28℃;05月07日(星期一):阴有阵雨或雷雨,温度19~25℃;05月08日(星期二):阴有阵雨,温度17~23℃。来源:金华气象、浦江天气,1
    7140,2d535d3f39104010b3a30344e06b8eb1,西北华北黄淮将有降雪 粤桂湘等地陷暴雨包围_中东部,原标题:西北华北黄淮将有降雪 粤桂湘等地陷暴雨包围\n昨日,内蒙古中东部、山西中北部、河北、北京、天津、山东西部和半岛北部、河南东北部及吉林南部、辽宁南部等地出现1~4毫米降雪或雨夹雪;安徽南部、浙江北部、江西北部等地部分地区降中到大雨。 \n预计16-18日,青海、甘肃中东部、陕西中南部、华北、黄淮等地将有一次降雪过程,降雪量以小到中雪为主。另外,15-17日,新疆西南部、西藏南部和东部局地有大雪或暴雪。 \n未来一周,南方地区多阴雨天气。其中,15-16日江南北部有中到大雨;17-18日降雨明显增强,广西东部和南部、广东西北部、湖南南部、江西西南部等地的部分地区有暴雨,局地大暴雨。 \n气象专家提醒,南方地区持续阴雨天气和部分地区的强降雨会对出行造成不利影响,请公众密切关注临近时段的天气预报和预警信息,外出注意携带雨具,开车出行的司机朋友们需注意防范降雨导致的道路湿滑等,谨慎慢行。 \n来源:中央气象台 返回搜狐,查看更多 责任编辑:,1
    9371,5704d6dde8b8421a9262c4c0f23c1d4c,气旋正在南太平洋翻腾!风暴“奥马”接近新西兰时发出红色警报,新西兰气象学家正在追踪一股向其北部移动的“加强”气旋,与此同时,南太平洋的一个邻近岛屿发布了强风和暴雨红色预警。新西兰气旋奥马路径:气旋正在南太平洋翻腾。热带气旋“奥马”于前两天在南太平洋形成,并于星期三升级为二级热带气旋。瓦努阿图是南太平洋的一个群岛,由80个岛屿组成。随着风暴的缓慢经过,瓦努阿图正遭受狂风暴雨的袭击。瓦努阿图气象局表示,未来12至24小时内,“时速75公里的破坏性大风”将威胁托尔巴、桑玛、佩纳马和马兰巴地区。在这些地区,红色警报仍然有效,岛民被警告有大雨和洪水。该警告还说:“受影响地区的海面将继续波涛汹涌。”“强烈建议人们,包括海船,在该系统撤离该地区之前不要出海。”“海洋强风警报和公海警报目前适用于北部和中部沿海及开放水域。”它位于桑托西南偏西240公里处,班克斯西南370公里处。热带风暴以每小时16公里的速度旋转,向西南偏南移动。新西兰气旋奥马轨迹:noaa追踪的风暴眼。accuweather说,在瓦努阿图的圣埃斯皮里图将感受到来自奥马最糟糕的情况。accuweather高级气象学家kristina pydynowski说:“本周末的降雨量将达到100-200毫米(4-8英寸),覆盖该岛西部山区。“北部山区可能会有300毫米(12英寸)的局部风暴。”奥马下一步的发展方向是什么?虽然新西兰目前还没有进入气象管理局的监测范围,但气象学家警告说,该国有30%的可能会遭遇强降雨。accuweather说,随着新喀里多尼亚继续向西南偏南移动,它可能在“下周初”进入风暴路径。新西兰气旋奥马路径:新喀里多尼亚可能面临危险的风和雨的威胁。这片法国领土由棕榈树环绕的海滩和令人惊叹的蓝色泻湖组成,是游客主要的水肺潜水目的地。pydynowski女士说:“奥马的准确轨迹将决定新喀里多尼亚是面临更大的暴雨和洪水风险,还是面临更严重的生命和财产风险。”如果风暴继续向西移动,而不是向南移动,新喀里多尼亚可能会躲过狂风暴雨。但是气象学家还不能确定它的路径,所以他们正在密切关注它下一步的动向。,1
    14535,2450de8920a5444696e10127dbade2ab,冷空气今日到货,还将带来大暴雨!接下来天天都是……_天气,原标题:冷空气今日到货,还将带来大暴雨!接下来天天都是……\n这一个春节\n有多少人是穿着短袖出了门\n毕竟在深圳全年无休的\n还有你那些夏天的衣服\n对于那些年前“耗资巨款”\n购入冬装的朋友来说\n内心也许是复杂的\n自1月中旬-2月上旬\n我市主要气候特点为\n“异常温暖,总体湿润”\n截至2月9日,月平均气温18.7℃\n刷新同期气温最高纪录\n月内未记录到\n最低气温10℃以下寒冷天气\n是为有气象记录以来首次\n并且2月16日全市最高气温\n更是普遍达到26℃-28℃\n2月16日全市最高气温分布\n眼看着近几日\n我国华北、华中等地\n又陆续开始下雪\n购入冬装的朋友\n或许仍未放弃希望\n还在继续追问着:\n“下一波大降温是什么时候?\n感觉冬装白买了!”\n其实,深圳入冬都没有成功\n还想着冬装呢?\n虽然几次寒潮都“入冬失败”\n现在春天已经快来了\n但冷空气还是不甘心退出舞台\n接下来冷暖空气交汇活跃\n天气将非常不稳定!\n甚至会带来强对流天气!\n这不,今天就有一股冷空气杀到!\n冷暖空气在广东上空“搞搞震”\n将带来降温和强降水!\n“大降温”暂时没有等来\n但2019年来首轮较明显降水过程\n却是越来越近了!\n久违的雨水要来了\n而且不是绵绵细雨\n而是大雨、暴雨!\n局地可达暴雨或大暴雨量级!\n随着偏东气流逐渐加强,天气开始出现转折,预计17日我市转阴天间多云,风力加大,沿海和高地偏东风最大可达8级左右,天气转凉;18-19日在高空槽影响下,我市有小到中雨局部大雨,雨时可能伴有零散雷暴,气温下降4-5℃;20-21日多云间阴天,有分散阵雨。\n总之,下周的天气\n可能总体不利于洗晒\n而且下周一刚好是开学的日子\n一整个寒假都天气晴好\n一到开学就下大暴雨\n莫不是老天抱怨寒假时间太短了?\n降雨对市民上学、上班和\n外出影响较大\n需提前安排好出行计划\n并做好防雨准备\n温馨提示\n(1)17日-18日沿海和高地最大阵风8级左右,港口码头、海上交通及其他海上活动需做好防风措施,市民注意远离海边并提前安置好户外物品;\n(2)今年以来我市气温偏高、降水偏少,建议水库利用本轮降水过程及时蓄水;\n(3)春运后期南方大部地区有雨雪,仍需关注对返程航空和陆路交通的影响。\n虽然本次降雨可能是\n今年首场比较明显的降雨\n但是触发“红暴停课”的条件\n可能性并不大\n同学们注意抓紧时间补作业噢!\n返回搜狐,查看更多 责任编辑:,1

    2.模型执行脚本参数设置

    --local_rank  -1 --model_type bert --model_name_or_path ../model/chinese_roberta_wwm_large_ext_pytorch  --do_train --do_eval --do_test --data_dir ../data/data_StratifiedKFold_42/data_origin_0 --output_dir     ../model/roberta_wwm_large_5121_lstmgru/roberta_wwm_large_5121_lstmgru_0  --max_seq_length 512 --split_num 2 --lstm_hidden_size 512 --lstm_layers 1 --lstm_dropout 0.1 --eval_steps 200 --per_gpu_train_batch_size 4 --gradient_accumulation_steps 4 --warmup_steps 0 --per_gpu_eval_batch_size 64 --learning_rate 1e-5 --adam_epsilon 1e-6 --weight_decay 0 --train_steps 1  --freeze 0

    --model_name_or_path 设置加载的roberta模型的路径
    --data_dir 设置数据的加载目录
    --output_dir 设置模型训练后保存的目录
    --split_num  设置将长文本分成n个块

    3. 将输入数据封装成模型的特征格式

    数据封装的相关代码如下:

    .......
            train_examples = read_examples(os.path.join(args.data_dir, 'train.csv'), is_training = True)
            train_features = convert_examples_to_features(
                train_examples, tokenizer, args.max_seq_length,args.split_num, True)
            all_input_ids = torch.tensor(select_field(train_features, 'input_ids'), dtype=torch.long)
            all_input_mask = torch.tensor(select_field(train_features, 'input_mask'), dtype=torch.long)
            all_segment_ids = torch.tensor(select_field(train_features, 'segment_ids'), dtype=torch.long)
            all_label = torch.tensor([f.label for f in train_features], dtype=torch.long)
            train_data = TensorDataset(all_input_ids, all_input_mask, all_segment_ids, all_label)
           
    ......

    其中 train_examples的示例如下:

     将输入数据进行特征转换的代码如下:

    def convert_examples_to_features(examples, tokenizer, max_seq_length, split_num,
                                     is_training):
        """Loads a data file into a list of `InputBatch`s."""
        # Swag is a multiple choice task. To perform this task using Bert,
        # we will use the formatting proposed in "Improving Language
        # Understanding by Generative Pre-Training" and suggested by
        # @jacobdevlin-google in this issue
        # https://github.com/google-research/bert/issues/38.
        #
        # Each choice will correspond to a sample on which we run the
        # inference. For a given Swag example, we will create the 4
        # following inputs:
        # - [CLS] context [SEP] choice_1 [SEP]
        # - [CLS] context [SEP] choice_2 [SEP]
        # - [CLS] context [SEP] choice_3 [SEP]
        # - [CLS] context [SEP] choice_4 [SEP]
        # The model will output a single value for each input. To get the
        # final decision of the model, we will run a softmax over these 4
        # outputs.
        features = []
        for example_index, example in enumerate(examples):
            context_tokens = tokenizer.tokenize(example.text_a)
            ending_tokens = tokenizer.tokenize(example.text_b)
    
            skip_len = len(context_tokens) / split_num
            choices_features = []
            index_begin = 0
            index_end = split_num - 1
            if example_index < 1 and is_training:
                logger.info("** RAW EXAMPLE **")
                logger.info("content: {}".format(context_tokens))
    
            for i in range(split_num):
                if i != index_end:
                    context_tokens_choice = context_tokens[int(i * skip_len):int((i + 1) * skip_len)]
                elif i == index_end:
                    context_tokens_choice = context_tokens[-int(i * skip_len):-1]
                _truncate_seq_pair(context_tokens_choice, ending_tokens, max_seq_length - 3, i == index_end)
    
                tokens = ["[CLS]"] + ending_tokens + ["[SEP]"] + context_tokens_choice + ["[SEP]"]
                segment_ids = [0] * (len(ending_tokens) + 2) + [1] * (len(context_tokens_choice) + 1)
                input_ids = tokenizer.convert_tokens_to_ids(tokens)
                input_mask = [1] * len(input_ids)
    
                padding_length = max_seq_length - len(input_ids)
                input_ids += ([0] * padding_length)
                input_mask += ([0] * padding_length)
                segment_ids += ([0] * padding_length)
                choices_features.append((tokens, input_ids, input_mask, segment_ids))
    
                label = example.label
                if example_index < 1 and is_training:
                    logger.info("*** Example ***")
                    logger.info("idx: {}".format(example_index))
                    logger.info("guid: {}".format(example.guid))
                    logger.info("tokens: {}".format(' '.join(tokens).replace('\u2581', '_')))
                    logger.info("input_ids: {}".format(' '.join(map(str, input_ids))))
                    logger.info("input_mask: {}".format(' '.join(map(str, input_mask))))
                    logger.info("segment_ids: {}".format(' '.join(map(str, segment_ids))))
                    logger.info("label: {}".format(label))
    
            features.append(
                InputFeatures(
                    example_id=example.guid,
                    choices_features=choices_features,
                    label=label
                )
            )
        return features
    

     context_tokens = tokenizer.tokenize(example.text_a):使用tokenizer分字器将第一句话的内容(text_a)进行分字,例如 ['作', '者', ':', '来', '源', ':', '吉', '安', '晚', '报', '字', '数', ':', '338'  .......
    ending_tokens = tokenizer.tokenize(example.text_b):使用tokenizer分字器将第一句话的标题(text_b)进行分字,例如['未', '来', '一', '周', '我', '市', '天', '气', '将', '持', '续', '阴', '雨']

    skip_len = len(context_tokens) / split_num:第一句话的内容有366个字,在程序执行的脚本中配置了长文本分块的参数(split_num=2),因此将第一句话的长文本内容切分成2块短文本,分别抽取特征。

    tokens = ["[CLS]"] + ending_tokens + ["[SEP]"] + context_tokens_choice + ["[SEP]"]:循环遍历split_num,将第一句的标题和第一句内容的第1块组合成一个特征,例如

    ['[CLS]', '未', '来', '一', '周', '我', '市', '天', '气', '将', '持', '续', '阴', '雨', '[SEP]', '作', '者', ':', '来', '源', ':', '吉', '安', '晚', '报', '字', '数', ':', '338', '本', '报', '讯', '记', '者', '报', '道', ':', '昨', '日', ',', '记', '者', '从', '市', '气', '象', '局', '获', '悉', ',', '未', '来', '一', '周', ',', '我', '市', '依', '然', '维', '持', '阴', '雨', '天', '气', ',', '气', '温', '偏', '低', '。', '15', '日', '我', '市', '降', '水', '较', '弱', ',', '部', '分', '小', '雨', ',', '8', '[UNK]', '10℃', ';', '16', '日', '受', '冷', '空', '气', '影', '响', ',', '气', '温'...

    及第一句的标题和第一句内容的第2块组合成一个特征,例如

    ['[CLS]', '未', '来', '一', '周', '我', '市', '天', '气', '将', '持', '续', '阴', '雨', '[SEP]', '21', '日', '阴', '转', '小', '雨', ',', '9', '[UNK]', '12', '##℃', '。', '气', '象', '部', '门', '提', '醒', '市', '民', ',', '17', '[UNK]', '18', '日', '降', '雨', '较', '明', '显', ',', '道', '路', '湿', '滑', '、', '能', '见', '度', '低', ',', '各', '地', '需', '防', '范', '地', '质', '灾', '害', '的', '发', '生', ',', '田', '间', '湿', '度', '大', '的', '油', '菜', '田', '块', ',', '应', '及', '时', '清', '理', '厢', '沟', '、', '腰', '沟', '和', '围', '沟', ',', '以', '利', '降', '湿', '排', '渍'...

    特征以[CLS]开头,内容与标题之间使用[SEP]分隔,注:[SEP]之后的内容不同,分别是长文本的两块内容。

    segment_ids = [0] * (len(ending_tokens) + 2) + [1] * (len(context_tokens_choice) + 1):A、B句子区分的特征。b句编码为0,a句编码为1.按照句子最大长度512进行补全,例如

    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...

    input_ids = tokenizer.convert_tokens_to_ids(tokens):从词汇表中查询字符的编码,

               

    将字符变为数字ID,按照句子最大长度512进行补全,例如

    [101, 3313, 3341, 671, 1453, 2769, 2356, 1921, 3698, 2199, 2898, 5330, 7346, 7433, 102, 8128, 3189, 7346, 6760, 2207, 7433, 8024, 130, 100, 8110, 8320, 511, 3698, 6496, 6956, 7305, 2990, 7008, 2356, 3696, 8024, 8126, 100, 8123, 3189, 7360, 7433, 6772, 3209, 3227, 8024, 6887, 6662, 3969, 3998, 510, 5543, 6224, 2428, 856, 8024, 1392, 1765, 7444, 7344, 5745, 1765, 6574, 4135, 2154, 4638, 1355, 4495, 8024, 4506, 7313, 3969, 2428, 1920, 4638, 3779, 5831, 4506, 1779, 8024, 2418, 1350, 3198, 3926, 4415, 1334, 3765, 510, 5587, 3765, 1469, 1741, 3765, 8024, 809, 1164, 7360, 3969, 2961, 3931...

    input_mask = [1] * len(input_ids):掩码默认值为1,按照句子最大长度512进行补全。

    以下分别为tokens, input_ids, input_mask, segment_ids的特征

    得到train_features如下:

    然后将input_ids, input_mask, segment_ids转换为Pytorch的数据结构torch.tensor。

    3.RoBERTa+LSTM+GRU模型

    • 模型初始化

           构建nn.GRU、nn.LSTM、nn.Linear模块。config.hidden_size需和模型维度适配。

          其他模型中如果要用BERTLayer隐藏层的状态信息,可设置config.output_hidden_states=True

    class BertForSequenceClassificationLSTMGRU(BertPreTrainedModel):
        r"""
            **labels**: (`optional`) ``torch.LongTensor`` of shape ``(batch_size,)``:
                Labels for computing the sequence classification/regression loss.
                Indices should be in ``[0, ..., config.num_labels - 1]``.
                If ``config.num_labels == 1`` a regression loss is computed (Mean-Square loss),
                If ``config.num_labels > 1`` a classification loss is computed (Cross-Entropy).
    
        Outputs: `Tuple` comprising various elements depending on the configuration (config) and inputs:
            **loss**: (`optional`, returned when ``labels`` is provided) ``torch.FloatTensor`` of shape ``(1,)``:
                Classification (or regression if config.num_labels==1) loss.
            **logits**: ``torch.FloatTensor`` of shape ``(batch_size, config.num_labels)``
                Classification (or regression if config.num_labels==1) scores (before SoftMax).
            **hidden_states**: (`optional`, returned when ``config.output_hidden_states=True``)
                list of ``torch.FloatTensor`` (one for the output of each layer + the output of the embeddings)
                of shape ``(batch_size, sequence_length, hidden_size)``:
                Hidden-states of the model at the output of each layer plus the initial embedding outputs.
            **attentions**: (`optional`, returned when ``config.output_attentions=True``)
                list of ``torch.FloatTensor`` (one for each layer) of shape ``(batch_size, num_heads, sequence_length, sequence_length)``:
                Attentions weights after the attention softmax, used to compute the weighted average in the self-attention heads.
    
        Examples::
    
            tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
            model = BertForSequenceClassification.from_pretrained('bert-base-uncased')
            input_ids = torch.tensor(tokenizer.encode("Hello, my dog is cute")).unsqueeze(0)  # Batch size 1
            labels = torch.tensor([1]).unsqueeze(0)  # Batch size 1
            outputs = model(input_ids, labels=labels)
            loss, logits = outputs[:2]
    
        """
    
        def __init__(self, config):
            super(BertForSequenceClassificationLSTMGRU, self).__init__(config)
            self.num_labels = config.num_labels
    
            self.config = config
            self.bert = BertModel(config)
            self.dropout = nn.Dropout(config.lstm_dropout)
            # self.pooling = nn.Linear(config.hidden_size, config.hidden_size)
            self.gru = nn.GRU(config.lstm_hidden_size*2, config.lstm_hidden_size,
                   #num_layers=1, bidirectional=True, batch_first=True).cuda()
                   num_layers = 1, bidirectional = True, batch_first = True)
            self.lstm = nn.LSTM(config.hidden_size, config.lstm_hidden_size,
                              #num_layers=1, bidirectional=True, batch_first=True).cuda()
                              num_layers = 1, bidirectional = True, batch_first = True)
            self.classifier = nn.Linear(config.hidden_size*4, self.config.num_labels)
            self.apply(self.init_weights)
    • 实现前向传播函数

          通过Bert模型(BertEmbeddings、BertEncoder、BertPooler)得到最后一个隐含层的信息(bert_output), 将bert_output连接更深层的双向LSTM、双向GRU模型,提取文本的更高维度特征,拼接平均池化(avg_pool), 隐含层状态(hh_gru), 最大池化(max_pool), BERT输出(pooled_output),然后进行分类。

     def forward(self, input_ids, token_type_ids=None, attention_mask=None, labels=None,
                    position_ids=None, head_mask=None):
    
            flat_input_ids = input_ids.view(-1, input_ids.size(-1))
            flat_position_ids = position_ids.view(-1, position_ids.size(-1)) if position_ids is not None else None
            flat_token_type_ids = token_type_ids.view(-1, token_type_ids.size(-1)) if token_type_ids is not None else None
            flat_attention_mask = attention_mask.view(-1, attention_mask.size(-1)) if attention_mask is not None else None
    
            outputs = self.bert(input_ids=flat_input_ids, position_ids=flat_position_ids,
                                token_type_ids=flat_token_type_ids,
                                attention_mask=flat_attention_mask, head_mask=head_mask)
            bert_output = outputs[0]
            pooled_output = outputs[1]
    
            h_lstm, _ = self.lstm(bert_output)  # [bs, seq, output*dir]
            h_gru, hh_gru = self.gru(h_lstm)    #
            hh_gru = hh_gru.view(-1, 2 * self.config.lstm_hidden_size)
    
            avg_pool = torch.mean(h_gru, 1)
            max_pool, _ = torch.max(h_gru, 1)
    
            # print(h_gru.shape, avg_pool.shape, hh_gru.shape, max_pool.shape, pooled_output.shape)
            h_conc_a = torch.cat(
                (avg_pool, hh_gru, max_pool, pooled_output), 1
            )
            # print(h_conc_a.shape)
    
            output = self.dropout(h_conc_a)
            logits = self.classifier(output)
    
            if labels is not None:
                if self.num_labels == 1:
                    #  We are doing regression
                    loss_fct = MSELoss()
                    loss = loss_fct(logits.view(-1), labels.view(-1))
                else:
                    loss_fct = CrossEntropyLoss()
                    loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
                outputs = [loss, ]
                outputs = outputs + [nn.functional.softmax(logits, -1)]
            else:
                outputs = nn.functional.softmax(logits, -1)
            return outputs  # (loss), logits, (hidden_states), (attentions)
    

    4.RoBERTa+LSTM+GRU模型训练

       RoBERTa+LSTM+GRU模型训练与BERT模型的训练类似,这里不再赘述。

     

    附录

    谷歌官方的BERT:https://github.com/google-research/bert

    Transformers: State-of-the-art Natural Language Processing for TensorFlow 2.0 and PyTorch. https://huggingface.co/transformers

    中文预训练BERT-wwm(Pre-Trained Chinese BERT with Whole Word Masking)https://github.com/ymcui/Chinese-BERT-wwm

    基线代码  https://github.com/cxy229/BDCI2019-SENTIMENT-CLASSIFICATION

                    https://github.com/duanzhihua/BDCI2019-SENTIMENT-CLASSIFICATION

     

    BERT+LSTM+GRU 模型结构:

    BertForSequenceClassificationLSTMGRU(
      (bert): BertModel(
        (embeddings): BertEmbeddings(
          (word_embeddings): Embedding(21128, 1024, padding_idx=0)
          (position_embeddings): Embedding(512, 1024)
          (token_type_embeddings): Embedding(2, 1024)
          (LayerNorm): BertLayerNorm()
          (dropout): Dropout(p=0.1)
        )
        (encoder): BertEncoder(
          (layer): ModuleList(
            (0): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (1): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (2): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (3): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (4): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (5): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (6): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (7): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (8): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (9): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (10): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (11): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (12): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (13): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (14): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (15): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (16): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (17): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (18): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (19): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (20): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (21): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (22): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
            (23): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): Linear(in_features=1024, out_features=1024, bias=True)
                  (key): Linear(in_features=1024, out_features=1024, bias=True)
                  (value): Linear(in_features=1024, out_features=1024, bias=True)
                  (dropout): Dropout(p=0.1)
                )
                (output): BertSelfOutput(
                  (dense): Linear(in_features=1024, out_features=1024, bias=True)
                  (LayerNorm): BertLayerNorm()
                  (dropout): Dropout(p=0.1)
                )
              )
              (intermediate): BertIntermediate(
                (dense): Linear(in_features=1024, out_features=4096, bias=True)
              )
              (output): BertOutput(
                (dense): Linear(in_features=4096, out_features=1024, bias=True)
                (LayerNorm): BertLayerNorm()
                (dropout): Dropout(p=0.1)
              )
            )
          )
        )
        (pooler): BertPooler(
          (dense): Linear(in_features=1024, out_features=1024, bias=True)
          (activation): Tanh()
        )
      )
      (dropout): Dropout(p=0.1)
      (gru): GRU(1024, 512, batch_first=True, bidirectional=True)
      (lstm): LSTM(1024, 512, batch_first=True, bidirectional=True)
      (classifier): Linear(in_features=4096, out_features=3, bias=True)
    )

     

     

    展开全文
  • NLP之情感分析:基于python编程(jieba库)实现中文文本情感分析(得到的是情感评分) 输出结果 1、测试对象 data1= '今天上海的天气真好!我的心情非常高兴!如果去旅游的话我会非常兴奋!和你一起去旅游我会更加...

    NLP之情感分析:基于python编程(jieba库)实现中文文本情感分析(得到的是情感评分)

     

     

     

     

    目录

    输出结果

    设计思路

    相关资料

    1、关于代码

    2、关于数据集

    关于留言

    1、留言内容的注意事项

    2、如何留言?

    2.1、第一种方法——在对应的博客下留言

    2.2、备用第二种方法——论坛发帖

    后续补充发放资料的说明

    主要部分代码实现


     

     

    输出结果

    1、测试对象
    data1= '今天上海的天气真好!我的心情非常高兴!如果去旅游的话我会非常兴奋!和你一起去旅游我会更加幸福!'
    data2= '今天上海天气真差,非常讨厌下雨,把我冻坏了,心情太不高兴了,不高兴,我真的很生气!'
    data3= '美国华裔科学家,祖籍江苏扬州市高邮县,生于上海,斯坦福大学物理系,电子工程系和应用物理系终身教授!'

    2、输出结果
    很明显,data1情感更加积极!data2情感消极!data3情感中等!

    [[240.0, 104.0, 8.3, 3.6, 8.0, 2.4]]
    [[0.0, 134.0, 0.0, 4.8, 0.0, 3.2]]
    [[2, 66, 0.1, 3.3, 0.4, 1.7]]
    [[2, 2, 0.1, 0.1, 0.4, 0.4]]

     

    设计思路

    后期更新……

     

     

    相关资料

    1、关于代码

    NLP之情感分析:基于python编程(jieba库)实现中文文本情感分析(得到的是情感评分)之全部代码
     

    2、关于数据集

    如需数据集,请留言向博主索取。
    :当前为学生身份的网友,可留言向博主索取。非学生身份的社会人士,请靠积分下载!

     

     

    关于留言

    1、留言内容的注意事项

    • 1、请新增评论,不要直接回复,折叠后,我容易看不到,会漏掉。
    • 2、请在前缀加一个索取资料的当天日期。
    • 3、切记要留下邮箱!!!

    比如留言:“20200307,早上10.11,你好,博主,我的邮箱是,我想索取……”

     


    2、如何留言?

    2.1、第一种方法——在对应的博客下留言

    即在本博客下直接留言即可!

     

    2.2、备用第二种方法——论坛发帖

    在我的论坛中发帖即可,我会及时回复。
    地址:https://bbs.csdn.net/topics/395531480

     

     

    后续补充发放资料的说明

    此类网友,太伤人心,这位网友,一定不是大学生,当代大学生的素质肯定比这位网友高的多。

     

    主要部分代码实现

    import jieba
    import numpy as np
    
    ……
    
    
    def sentiment_score_list(dataset):
        seg_sentence = dataset.split('。')
    
        count1 = []
        count2 = []
        for sen in seg_sentence: #循环遍历每一个评论
            segtmp = jieba.lcut(sen, cut_all=False)  #把句子进行分词,以列表的形式返回
            i = 0 #记录扫描到的词的位置
            a = 0 #记录情感词的位置
            poscount = 0 #积极词的第一次分值
            poscount2 = 0 #积极词反转后的分值
            poscount3 = 0 #积极词的最后分值(包括叹号的分值)
            negcount = 0
            negcount2 = 0
            negcount3 = 0
            for word in segtmp:
                if word in posdict:  # 判断词语是否是情感词
                    poscount += 1
                    c = 0
                    for w in segtmp[a:i]:  # 扫描情感词前的程度词
                        if w in mostdict:
                            poscount *= 4.0
                        elif w in verydict:
                            poscount *= 3.0
                        elif w in moredict:
                            poscount *= 2.0
                        elif w in ishdict:
                            poscount *= 0.5
                        elif w in deny_word:
                            c += 1
                    if judgeodd(c) == 'odd':  # 扫描情感词前的否定词数
                        poscount *= -1.0
                        poscount2 += poscount
                        poscount = 0
                        poscount3 = poscount + poscount2 + poscount3
                        poscount2 = 0
                    else:
                        poscount3 = poscount + poscount2 + poscount3
                        poscount = 0
                    a = i + 1  # 情感词的位置变化
    
                elif word in negdict:  # 消极情感的分析,与上面一致
                    negcount += 1
                    d = 0
                    for w in segtmp[a:i]:
                        if w in mostdict:
                            negcount *= 4.0
                        elif w in verydict:
                            negcount *= 3.0
                        elif w in moredict:
                            negcount *= 2.0
                        elif w in ishdict:
                            negcount *= 0.5
                        elif w in degree_word:
                            d += 1
                    if judgeodd(d) == 'odd':
                        negcount *= -1.0
                        negcount2 += negcount
                        negcount = 0
                        negcount3 = negcount + negcount2 + negcount3
                        negcount2 = 0
                    else:
                        negcount3 = negcount + negcount2 + negcount3
                        negcount = 0
                    a = i + 1
                elif word == '!' or word == '!':  ##判断句子是否有感叹号
                    for w2 in segtmp[::-1]:  # 扫描感叹号前的情感词,发现后权值+2,然后退出循环
                        if w2 in posdict or negdict:
                            poscount3 += 2
                            negcount3 += 2
                            break
                i += 1 # 扫描词位置前移
    
    
                # 以下是防止出现负数的情况
                pos_count = 0
                neg_count = 0
                if poscount3 < 0 and negcount3 > 0:
                    neg_count += negcount3 - poscount3
                    pos_count = 0
                elif negcount3 < 0 and poscount3 > 0:
                    pos_count = poscount3 - negcount3
                    neg_count = 0
                elif poscount3 < 0 and negcount3 < 0:
                    neg_count = -poscount3
                    pos_count = -negcount3
                else:
                    pos_count = poscount3
                    neg_count = negcount3
    
                count1.append([pos_count, neg_count])
            count2.append(count1)
            count1 = []
    
        return count2
    
    def sentiment_score(senti_score_list):
        score = []
        for review in senti_score_list:
            score_array = np.array(review)
            Pos = np.sum(score_array[:, 0])
            Neg = np.sum(score_array[:, 1])
            AvgPos = np.mean(score_array[:, 0])
            AvgPos = float('%.1f'%AvgPos)
            AvgNeg = np.mean(score_array[:, 1])
            AvgNeg = float('%.1f'%AvgNeg)
            StdPos = np.std(score_array[:, 0])
            StdPos = float('%.1f'%StdPos)
            StdNeg = np.std(score_array[:, 1])
            StdNeg = float('%.1f'%StdNeg)
            score.append([Pos, Neg, AvgPos, AvgNeg, StdPos, StdNeg])
        return score
    
    
    
    data1= '今天上海的天气真好!我的心情非常高兴!如果去旅游的话我会非常兴奋!和你一起去旅游我会更加幸福!'
    data2= '今天上海天气真差,非常讨厌下雨,把我冻坏了,心情太不高兴了,不高兴,我真的很生气!'
    data3= '美国华裔科学家,祖籍江苏扬州市高邮县,生于上海,斯坦福大学物理系,电子工程系和应用物理系终身教授!'
    print(sentiment_score(sentiment_score_list(data1)))
    print(sentiment_score(sentiment_score_list(data2)))
    print(sentiment_score(sentiment_score_list(data3)))

     

    展开全文
  • NLP ---文本情感分析

    万次阅读 多人点赞 2019-01-03 10:34:46
    前面几节我们详细的研究了文本分析的相关算法,本节将简单介绍一下基于文本的情感分析方面的内容,本节更多是论述方面的内容,这个方向的算法基本都是我们前面学过的,原理就不详细的讲解了,如果有感兴趣的朋友可以...

    前面几节我们详细的研究了文本分析的相关算法,本节将简单介绍一下基于文本的情感分析方面的内容,本节更多是论述方面的内容,这个方向的算法基本都是我们前面学过的,原理就不详细的讲解了,如果有感兴趣的朋友可以自行查阅资料进行研究,这里就不在详细的讲解了,以后如果工作中遇到相关的在详细的研究一下,下面正式开始本节的内容:

    情感分析简述

    文本情感分析(Sentiment Analysis)是指利用自然语言处理和文本挖掘技术,对带有情感色彩的主观性文本进行分析、处理和抽取的过程[1]。目前,文本情感分析研究涵盖了包括自然语言处理、文本挖掘、信息检索、信息抽取、机器学习和本体学等多个领域,得到了许多学者以及研究机构的关注,近几年持续成为自然语言处理和文本挖掘领域研究的热点问题之一。情感分析任务按其分析的粒度可以分为篇章级,句子级,词或短语级;按其处理文本的类别可分为基于产品评论的情感分析和基于新闻评论的情感分析;按其研究的任务类型,可分为情感分类,情感检索和情感抽取等子问题[2]。文本情感分析的基本流程如图 1 所示,包括从原始文本爬取,文本预处理,语料库和情感词库构建以及情感分析结果等全流程。由于文本原始素材爬取,分词等预处理技术已比较成熟,本文接下来将通过情感分析的主要任务情感分类,情感检索,情感抽取问题来分析和阐述已有的相关研究工作。

    情感分类

    情感分类又称情感倾向性分析,是指对给定的文本,识别其中主观性文本的倾向是肯定还是否定的,或者说是正面还是负面的,是情感分析领域研究最多的。通常网络文本存在大量的主观性文本和客观性文本。客观性文本是对事物的客观性描述,不带有感情色彩和情感倾向,主观性文本则是作者对各种事物的看法或想法,带有作者的喜好厌恶等情感倾向。情感分类的对象是带有情感倾向的主观性文本,因此情感分类首先要进行文本的主客观分类。文本的主客观分类主要以情感词识别为主,利用不同的文本特征表示方法和分类器进行识别分类,对网络文本事先进行主客观分类,能够提高情感分类的速度和准确度[3]。纵观目前主观性文本情感倾向性分析的研究工作,主要研究思路分为基于语义的情感词典方法和基于机器学习的方法。

    基于语义的情感词典方法

    (1) 构建词典

    情感词典的构建是情感分类的前提和基础,目前在实际使用中,可将其归为 4 类:通用情感词、程度副词、否定词
    、领域词。目前国内外,情感词典的构建方法主要是利用已有电子词典扩展生成情感词典。英文方面主要是基于对英文词典 的Word  Net 的扩充,Hu 和 Liu[4]在已手工建立种子形容词词汇表的基础上,利用 World Net 中词间的同义和近义关系判断情感词的情感倾向,并以此来判断观点的情感极性。中文方面则主要是对知网 How net 的扩充,朱嫣岚[5]利用语义相似度计算方法计算词语与基准情感词集的语义相似度,以此推断该词语的情感倾向。此外,还可以建立专门的领域词典,以提高情感分类的准确性。

    (2)构建倾向性计算算法

    基于语义的情感词典的倾向性计算不同于所需大量训练数据集的机器学习算法,主要是利用情感词典及句式词库分析文本语句的特殊结构及情感倾向词,采用权值算法代替传统人工判别或仅利用简单统计的方法进行情感分类。给情感强度不同的情感词赋予不同权值,然后进行加权求和。文献[6]利用加权平均算法式(1)计算,可有效提高通用领域情感分类的效率和准确率

    其中,\large N_p,N_n分别代表表达正面情感和负面情感的词汇数目;\large wp_i,wp_j分别代表正面情感词汇和负面情感词汇的权值。

    (3)确定阈值来判断文本倾向性

    一般情况下,加权计算结果为正是正面倾向,结果为负是负面倾向 ,得分为零无倾向。所得结果评价一般采用自然语言中
    经常使用的正确率、召回率和 F 值来评判算法效果。
    基于情感词典的方法和基于机器学习的分类算法相比,虽属于粗粒度的倾向性分类方法,但由于不依赖标注好的训练集,实现相对简单,对于普遍通用领域的网络文本可有效快速地进行情感分类。

    基于机器学习的情感分类方法

    文本情感倾向性分析与传统的基于主题的文本分类相似但有所不同,基于主题的文本分类是把文本分类到各个预定义的主题上,如军事,互联网,政治,体育等,而情感分类不是基于内容本身的,而是按照文本持有的情感、态度进行判断。现有任何机器学习的分类方法都可以用到情感分类中来。基于机器学习的情感分类,其大致流程如下:首先人工标注文本倾向性作为训练集,提取文本情感特征,通过机器学习的方法构造情感分类器,待分类的文本通过分类器进行倾向性分类。常用的情感分类特征包括情感词,词性,句法结构,否定表达模板,连接,语义话题等[7],研究者通过挖掘各种不同的特征以期望提高情感分类的能。常用的特征提取方法有信息增益( Information Gain,IG),CHI 统计量( Chi - square,CHI) 和文档频率( Document Frequency,DF) 等。常用的分类方法有中心向量分类方法、K - 近邻(K - Nearest - Neighbor,KNN) 分类方法、贝叶斯分类器、支持向量机、条件随机场、最大熵分类器等。
    最早从事情感分析研究的 Pang 等人[8]使用词袋(Bag - of - Feature) 框架选定文本的 N 元语法( N -Gram) 和词性( POS) 等作为情感 uo 特征,使用有监督的机器学习的方法将电影评论分为正向和负向两类,分别使用朴素贝叶斯,最大熵模型和支持向量机作为有监督学习算法的分类器。结果显示支持向量机在几种分类方法中效果最好,分类准确率达到 80% 。文本情感分类的准确率难以达到普通文本分类的水平,主要是情感文本中复杂的情感表达和大量的情感歧义造成的。在基于机器学习的情感分类算法中,每篇文章被转换成一个对应的特征向量来表示。特征选择的好坏将直接影响情感分析任务的性能。在 Pang 等人的研究基础上,后续研究主要是把情感分类作为一个特征优化任务[9- 11]。随着语义特征信息的加入和训练语料库的发展,基于机器学习的分类将会有广阔的发展前景。

    情感检索

    情感检索是从海量文本中查询到观点信息,根据主题相关度和观点倾向性对结果排序。情感检索返回的结果要同时满足主题相关和带有情感倾向或指定的情感倾向,是比情感分类更为复杂的任务。主题相关度和观点倾向性对结果排序,随着人们网络检索需求的增高,在传统搜索中加入情感倾向成了搜索技术中一个新的研究热点。和传统的互联网搜索相似,情感检索有两个主要任务:(1) 检索和查询相关的文档或句子。(2)对检索的相关文档或句子进行排序。与传统搜索不同的是互联网搜索的任务只要求找到和查询相关的文档和句子,而情感检索还要确定文档和句子是否表达了观点,以及观点是正面的或是负面的。目前情感检索主要实现方法有两种:一是按传统信息检索模型进行主题相关的文档检索,对检索结果进行情感分类;另一种是同时计算主题相关值和情感倾向值进行检索。第一种方法一般使用传统的检索模型以及较为成熟的查询扩展技术,然后用情感分类方法进行倾向性计
    算。文献[12 ~ 13]给出的情感检索系统是国际文本检索会议 TREC(Text Retrieval Evaluation Conference)博客观点搜索任务的优胜者,该系统分为两部分检索部分和观点分类部分。检索部分完成传统的信息检索任务,同时在处理用户查询时将用户查询中的概念进行识别和消歧义,对于每个搜索查询进行同义词扩展,使用概念和关键字针对扩展后的查询对每个文档计算一个相似度,查询的关键字和文档的相关度是这两种相似度的综合。观点分类部分使用监督学习的方法使用两个分类器将文档分为两个类别带观点和不带观点的,带观点的文档再分为正面,负面或者混合的观点。第一个分类器训练数据是从评价网站包括 rateilt-
    all. com 和 epinion. com 收集大量带观点的数据和从维基百科等客观性网站收集不带观点的训练数据。第二个分类器训练数据来自评论网站包含打分的评论,低的打分表明负面观点,高的打分表明正面观点。这里两种监督学习的分类器都采用支持向量机。在 TREC博客检索数据集研究的基础上,研究者采用不同的情感分类方法开展了后续研究[14 - 16]。

    上面的方法是将检索和情感分类独立计算的,实际中主题相关和情感匹配是有关联的,需要同时计算主题相关和情感匹配,这是因为不同的情感词在文档中对不同的查询词下可能有相反的情感倾向。第二种方法则是同时考虑主题相关和情感文档排序,选择排序策略时需要同时兼顾。很多学者[17 - 18]对排序策略进行了研究,一般是分别计算情感倾向值和查询相关度值,然后加权求和进行排序。Zhang 等人[19]提出一种融合文档情感得分和文档查询相关度得分的概率生成模型排序方法,取得了理想的效果。
    情感信息检索是传统信息检索技术和情感分析技术的融合,如何更好的融合二者得到理想的情感检索结果是未来要重点关注的。

    情感抽取

    情感抽取是指抽取情感文本中有价值的情感信息,其要判断一个单词或词组在情感表达中扮演的角色,包括情感表达者识别,评价对象识别,情感观点词识别等任务。情感表达者识别又称观点持有者抽取,其是观点、评论的隶属者。在社交媒体和产品评论中,观点持有者通常是文本的作者或者评论员,其的登录账号是可见的,观点持有者抽取比较简单。而对于新闻文章和其他一些表达观点的任务或者组织显式的出现在文档时,观点持有者一般则是由机构名或人名组成,所以可采用命名实体识别方法进行抽取。Kim[20]等人借助语义角色标注来完成观点持有者的抽取。然而这些处理方法会导致较低的语言覆盖现象和较差的领域适应性,可以通过基于模式识别的信息抽取 ( Information Extraction) 和 机 器 学 习 ( Machine Learning )技 术 来解决[21]。评价对象和情感词抽取在情感分析中具有重要作用。利用评价对象和情感词的抽取,可以构建领域相关的主题词表和情感词表,情感词表的构建在情感分类部分已做阐述。评价对象是指某段评论中的主题,是评论文本中评价词语修饰的对象,现有的研究大多
    将评价对象限定在名词或名词短语的范畴内,一般使用基于模板和规则的方法抽取评价对象。规则的制定通常基于一系列的语言分析和预处理过程,命名实体识别,词性标注和句法分析等方法[22 - 25]都被用来进行评价对象抽取。文献[26]便是使用 3 条限制等级逐渐渐进的词性规则从评价对象集中抽取评价对象,取得了较好的结果。
    情感抽取是情感分析的基础任务,通过对大量的情感文本分析,有价值的情感信息抽取对于情感分析的上层任务情感检索和情感分类有直接帮助,如何准确抽取情感信息一直都是研究者关注的重点。

    文本情感分析评测

    近年来,情感分析得到了越来越多研究机构和学者的关注,在 SIGIR、ACL、WWW、CIKM、WSDM 等著名国际会议上,针对这一问题的研究成果层出不穷[27],国内外研究机构组织了众多相关评测来推动情感分析技术的发展。

    由国际文本检索会议 TREC 针对英文文本观点检索任务的博客检索任务(Blog Track),篇章情感分类任务,以及其他一些有趣的情感分析任务;由日本国立信息学研究所主办的搜索引擎评价国际会议 NTCIR(NIITest Collection for IR Systems) 针对日、韩、英、中文文本的情感分类以及观点持有者抽取任务。由中文信息学会信息检索委员会主办的每年一次的中文倾向性分析评测 C
    OAE(Chinese Opinion Analysis Evaluation) 已举办了 5 届,在关注情感词语和观点句子的抽取以及倾向性识别的基础上重点对于否定句、比较句以及微博观点句进行评测[28]。众多研究机构的评测推动了情感分析研究的发展,出现了很多有代表性的情感分析语料库资源,文献[29 ~ 30]对语料库构建进行了详细阐述,如康奈尔影评数据集(Cornell Movie - Review Datasets),多视角问答( Multiple - Perspective Question Answering,MPQA)语料库,TREC 测试集,NTCIR 多语言语料库(
     NTCIRmultilingual corpus),中文 COAE 语料库等。

    参考文献

    [1] PANG B,LEE L. Opinion mining and sentiment analysis[J].Foundations and Trends in InformationRetrieval,2008,2 (1 -2) :130 - 135.
    [2] 赵妍妍,秦兵,刘挺,等. 文本倾向性分析[J]. 软件学报,2010,21(8) :1834 - 1848.
    [3] 厉小军,戴霖,施寒潇,等. 文本倾向性分析综述[J]. 浙江大学学报,2011,45(7):1167 - 1175.
    [4] HU M,LIU B. Mining and summarizing customer reviews[C]. NY,USA:Proceedings of Knowledge Discoveryand Da-ta Mining,2004:168 - 177.
    [5] 朱嫣岚,闵锦,周雅倩,等. 基于 How Net 的词汇语义倾向计算[J]. 中文信息学报,2006,20(1):14 - 20.
    [6] 张昊旻,石博莹,刘栩宏. 基于权值算法的中文情感分析系统研究与实现[J]. 计算机应用研究,201229 (12):4571 - 4573.

    [7] 李方涛. 基于产品评论的情感分析研究[D]. 北京:清华大学,2011.

    [8] PANG B,LEE L,VAITHYANATHAN S. Thumbs up:senti-ment classification using machine learning techniques [
    C].PA,USA:Proceedings of the ACL - 02 Conference on Empir-ical methods in natural language processing - Volume 10,Stroudsburg,Association for Computational Linguistics,2002:79 - 86.
    [9] MELVILLE P,GRYC W,LAWRENCE. Sentiment analysis ofblogs by combining lexical knowledge with text classification[C]. New York:Proceedings of SIGKDD,ACM,2009.
    [10] LI S,HUANG C,ZHOU G. Employing personal impersonal viewsin supervised and semisupervised sentiment classification [C].New York:Proceedings of ACL,ACM,2010:414 - 423.
    [11] KUMAR A,SEBASTIAN T M. Sentiment analysis on twitter[J]. International Journal of Computer ScienceIssues,2012,9(4) :628 - 633.
    [12] ZHANG W,YU C,MENG W. Opinion retrieval from blogs[C]. Proceedings of the Sixteenth ACM Conferenceon Con-ference on Information and Knowledge Management,ACM,2007:831 - 840.
    [13] ZHANG W,JIA L,YU C,et al. Improve the effectiveness ofthe opinion retrieval and opinion polarity classification [C].MA USA:Proceedings of the 17th ACM Conference on Infor-mation and Knowledge Management,ACM,2008:1415 - 1416.

    主要参考了:文本情感分析研究综述  马 力1,宫玉龙2
     

    展开全文
  • 自然语言处理实战——LSTM情感分析

    千人学习 2018-11-06 16:20:40
    自然语言处理实战视频教程,自然语言处理中重要的算法,词向量模型。本课程从语言模型入手,详解词向量构造原理与求解算法。理论与实战结合, 基于深度学习主流框架Tensorflow实例演示如何用深度学习来进行文本分类...
  • 自然语言处理6 -- 情感分析

    万次阅读 多人点赞 2020-07-01 09:28:07
    情感分析是自然语言处理中常见的场景,比如淘宝商品评价,饿了么外卖评价等,对于指导产品更新迭代具有关键性作用。通过情感分析,可以挖掘产品在各个维度的优劣,从而明确如何改进产品。比如对外卖评价,可以分析菜...

    系列文章,欢迎阅读
    NLP预训练模型1 – 综述
    NLP预训练模型2 – BERT详解和源码分析
    Transformer家族1 – Transformer详解和源码分析

    自然语言处理1 – 分词
    自然语言处理2 – jieba分词用法及原理
    自然语言处理3 – 词性标注
    自然语言处理4 – 句法分析
    自然语言处理5 – 词向量
    自然语言处理6 – 情感分析

    1 概述

    情感分析是自然语言处理中常见的场景,比如淘宝商品评价,饿了么外卖评价等,对于指导产品更新迭代具有关键性作用。通过情感分析,可以挖掘产品在各个维度的优劣,从而明确如何改进产品。比如对外卖评价,可以分析菜品口味、送达时间、送餐态度、菜品丰富度等多个维度的用户情感指数,从而从各个维度上改进外卖服务。

    情感分析可以采用基于情感词典的传统方法,也可以采用基于深度学习的方法,下面详细讲解

    2 基于情感词典的传统方法

    2.1 基于词典的情感分类步骤

    基于情感词典的方法,先对文本进行分词和停用词处理等预处理,再利用先构建好的情感词典,对文本进行字符串匹配,从而挖掘正面和负面信息。如下图
    在这里插入图片描述

    2.2 情感词典

    情感词典包含正面词语词典、负面词语词典、否定词语词典、程度副词词典等四部分。如下图
    在这里插入图片描述

    词典包含两部分,词语和权重,如下

    正面:
    很快  1.75
    挺快  1.75
    还好  1.2
    很萌  1.75
    服务到位    1
    
    负面:
    无语  2
    醉了  2
    没法吃  2
    不好  2
    太差  5
    太油  2.5
    有些油  1
    咸   1
    一般  0.5
    
    程度副词:
    超级  2
    超  2
    都   1.75
    还   1.5
    实在  1.75
    
    否定词:
    不   1
    没   1
    无   1
    非   1
    莫   1
    弗   1
    毋   1
    

    情感词典在整个情感分析中至关重要,所幸现在有很多开源的情感词典,如BosonNLP情感词典,它是基于微博、新闻、论坛等数据来源构建的情感词典,以及知网情感词典等。当然我们也可以通过语料来自己训练情感词典。

    2.3 情感词典文本匹配算法

    基于词典的文本匹配算法相对简单。逐个遍历分词后的语句中的词语,如果词语命中词典,则进行相应权重的处理。正面词权重为加法,负面词权重为减法,否定词权重取相反数,程度副词权重则和它修饰的词语权重相乘。如下图

    在这里插入图片描述

    利用最终输出的权重值,就可以区分是正面、负面还是中性情感了。

    2.4 缺点

    基于词典的情感分类,简单易行,而且通用性也能够得到保障。但仍然有很多不足

    1. 精度不高。语言是一个高度复杂的东西,采用简单的线性叠加显然会造成很大的精度损失。词语权重同样不是一成不变的,而且也难以做到准确。
    2. 新词发现。对于新的情感词,比如给力,牛逼等等,词典不一定能够覆盖
    3. 词典构建难。基于词典的情感分类,核心在于情感词典。而情感词典的构建需要有较强的背景知识,需要对语言有较深刻的理解,在分析外语方面会有很大限制。

    3 基于深度学习的算法

    近年来,深度学习在NLP领域内也是遍地开花。在情感分类领域,我们同样可以采用深度学习方法。基于深度学习的情感分类,具有精度高,通用性强,不需要情感词典等优点。

    3.1 基于深度学习的情感分类步骤

    基于深度学习的情感分类,首先对语句进行分词、停用词、简繁转换等预处理,然后进行词向量编码,然后利用LSTM或者GRU等RNN网络进行特征提取,最后通过全连接层和softmax输出每个分类的概率,从而得到情感分类。

    在这里插入图片描述

    3.2 代码示例

    下面通过代码来讲解这个过程。下面是我周末写的,2018年AI Challenger细粒度用户评论情感分析比赛中的代码。项目数据来源于大众点评,训练数据10万条,验证1万条。分析大众点评用户评论中,关于交通,菜品,服务等20个维度的用户情感指数。分为正面、负面、中性和未提及四类。代码在验证集上,目前f1 socre可以达到0.62。

    3.2.1 分词和停用词预处理

    数据预处理都放在了PreProcessor类中,主函数是process。步骤如下

    1. 读取原始csv文件,解析出原始语句和标注
    2. 错别字,繁简体,拼音,语义不明确等词语的处理
    3. stop words停用词处理
    4. 分词,采用jieba分词进行处理。分词这儿有个trick,由于分词后较多口语化的词语不在词向量中,所以对这部分词语从jieba中del掉,然后再进行分词。直到只有为数不多的词语不在词向量中为止。
    5. 构建词向量到词语的映射,并对词语进行数字编码。这一步比较常规。
    class PreProcessor(object):
        def __init__(self, filename, busi_name="location_traffic_convenience"):
            self.filename = filename
            self.busi_name = busi_name
            self.embedding_dim = 256
    
            # 读取词向量
            embedding_file = "./word_embedding/word2vec_wx"
            self.word2vec_model = gensim.models.Word2Vec.load(embedding_file)
    
        # 读取原始csv文件
        def read_csv_file(self):
            reload(sys)
            sys.setdefaultencoding('utf-8')
            print("after coding: " + str(sys.getdefaultencoding()))
    
            data = pd.read_csv(self.filename, sep=',')
            x = data.content.values
            y = data[self.busi_name].values
    
            return x, y
    
        # todo 错别字处理,语义不明确词语处理,拼音繁体处理等
        def correct_wrong_words(self, corpus):
            return corpus
    
        # 去掉停用词
        def clean_stop_words(self, sentences):
            stop_words = None
            with open("./stop_words.txt", "r") as f:
                stop_words = f.readlines()
                stop_words = [word.replace("\n", "") for word in stop_words]
    
            # stop words 替换
            for i, line in enumerate(sentences):
    
                for word in stop_words:
                    if word in line:
                        line = line.replace(word, "")
                sentences[i] = line
    
            return sentences
    
        # 分词,将不在词向量中的jieba分词单独挑出来,他们不做分词
        def get_words_after_jieba(self, sentences):
            # jieba分词
            all_exclude_words = dict()
            while (1):
                words_after_jieba = [[w for w in jieba.cut(line) if w.strip()] for line in sentences]
                # 遍历不包含在word2vec中的word
                new_exclude_words = []
                for line in words_after_jieba:
                    for word in line:
                        if word not in self.word2vec_model.wv.vocab and word not in all_exclude_words:
                            all_exclude_words[word] = 1
                            new_exclude_words.append(word)
                        elif word not in self.word2vec_model.wv.vocab:
                            all_exclude_words[word] += 1
    
                # 剩余未包含词小于阈值,返回分词结果,结束。否则添加到jieba del_word中,然后重新分词
                if len(new_exclude_words) < 10:
                    print("length of not in w2v words: %d, words are:" % len(new_exclude_words))
                    for word in new_exclude_words:
                        print word,
                    print("\nall exclude words are: ")
                    for word in all_exclude_words:
                        if all_exclude_words[word] > 5:
                            print "%s: %d," % (word, all_exclude_words[word]),
                    return words_after_jieba
                else:
                    for word in new_exclude_words:
                        jieba.del_word(word)
    
            raise Exception("get_words_after_jieba error")
    
        # 去除不在词向量中的词
        def remove_words_not_in_embedding(self, corpus):
            for i, sentence in enumerate(corpus):
                for word in sentence:
                    if word not in self.word2vec_model.wv.vocab:
                        sentence.remove(word)
                        corpus[i] = sentence
    
            return corpus
    
        # 词向量,建立词语到词向量的映射
        def form_embedding(self, corpus):
            # 1 读取词向量
            w2v = dict(zip(self.word2vec_model.wv.index2word, self.word2vec_model.wv.syn0))
    
            # 2 创建词语词典,从而知道文本中有多少词语
            w2index = dict()        # 词语为key,索引为value的字典
            index = 1
            for sentence in corpus:
                for word in sentence:
                    if word not in w2index:
                        w2index[word] = index
                        index += 1
            print("\nlength of w2index is %d" % len(w2index))
    
            # 3 建立词语到词向量的映射
            # embeddings = np.random.randn(len(w2index) + 1, self.embedding_dim)
            embeddings = np.zeros(shape=(len(w2index) + 1, self.embedding_dim), dtype=float)
            embeddings[0] = 0   # 未映射到的词语,全部赋值为0
    
            n_not_in_w2v = 0
            for word, index in w2index.items():
                if word in self.word2vec_model.wv.vocab:
                    embeddings[index] = w2v[word]
                else:
                    print("not in w2v: %s" % word)
                    n_not_in_w2v += 1
            print("words not in w2v count: %d" % n_not_in_w2v)
    
            del self.word2vec_model, w2v
    
            # 4 语料从中文词映射为索引
            x = [[w2index[word] for word in sentence] for sentence in corpus]
    
            return embeddings, x
    
        # 预处理,主函数
        def process(self):
            # 读取原始文件
            x, y = self.read_csv_file()
    
            # 错别字,繁简体,拼音,语义不明确,等的处理
            x = self.correct_wrong_words(x)
    
            # stop words
            x = self.clean_stop_words(x)
    
            # 分词
            x = self.get_words_after_jieba(x)
    
            # remove不在词向量中的词
            x = self.remove_words_not_in_embedding(x)
    
            # 词向量到词语的映射
            embeddings, x = self.form_embedding(x)
    
            # 打印
            print("embeddings[1] is, ", embeddings[1])
            print("corpus after index mapping is, ", x[0])
            print("length of each line of corpus is, ", [len(line) for line in x])
    
            return embeddings, x, y
    

    3.2.2 词向量编码

    词向量编码步骤主要有:

    1. 加载词向量。词向量可以从网上下载或者自己训练。网上下载的词向量获取简单,但往往缺失特定场景的词语。比如大众点评菜品场景下的鱼香肉丝、干锅花菜等词语,而且往往这些词语在特定场景下还十分重要。而自己训练则需要几百G的语料,在高性能服务器上连续训练好几天,成本较高。可以将两种方法结合起来,也就是加载下载好的词向量,然后利用补充语料进行增量训练。
    2. 建立词语到词向量的映射,也就是找到文本中每个词语的词向量
    3. 对文本进行词向量编码,可以通过keras的Embedding函数,或者其他深度学习库来搞定。
      前两步在上面代码中已经展示了,词向量编码代码示例如下
    Embedding(input_dim=len(embeddings),
                        output_dim=len(embeddings[0]),
                        weights=[embeddings],
                        input_length=self.max_seq_length,
                        trainable=False,
                        name=embeddings_name))
    

    3.2.3 构建LSTM网络

    LSTM网络主要分为如下几层

    1. 两层的LSTM。
    2. dropout,防止过拟合
    3. 全连接,从而可以输出类别
    4. softmax,将类别归一化到[0, 1]之间
      LSTM网络是重中之重,这儿可以优化的空间很大。比如可以采用更优的双向LSTM,可以加入注意力机制。这两个trick都可以提高最终准确度。另外可以建立分词和不分词两种情况下的网络,最终通过concat合并。
    class Model(object):
        def __init__(self, busi_name="location_traffic_convenience"):
            self.max_seq_length = 100
            self.lstm_size = 128
            self.max_epochs = 10
            self.batch_size = 128
    
            self.busi_name = busi_name
            self.model_name = "model/%s_seq%d_lstm%d_epochs%d.h5" % (self.busi_name, self.max_seq_length, self.lstm_size, self.max_epochs)
            self.yaml_name = "model/%s_seq%d_lstm%d_epochs%d.yml" % (self.busi_name, self.max_seq_length, self.lstm_size, self.max_epochs)
    
        def split_train_data(self, x, y):
            x_train, x_val, y_train, y_val = train_test_split(x, y, test_size=0.1)
    
            # 超长的部分设置为0,截断
            x_train = sequence.pad_sequences(x_train, self.max_seq_length)
            x_val = sequence.pad_sequences(x_val, self.max_seq_length)
    
            # y弄成4分类,-2未提及,-1负面,0中性,1正面
            y_train = keras.utils.to_categorical(y_train, num_classes=4)
            y_val = keras.utils.to_categorical(y_val, num_classes=4)
    
            return x_train, x_val, y_train, y_val
    
        def build_network(self, embeddings, embeddings_name):
            model = Sequential()
            model.add(Embedding(input_dim=len(embeddings),
                                output_dim=len(embeddings[0]),
                                weights=[embeddings],
                                input_length=self.max_seq_length,
                                trainable=False,
                                name=embeddings_name))
            model.add(LSTM(units=self.lstm_size, activation='tanh', return_sequences=True, name='lstm1'))
            model.add(LSTM(units=self.lstm_size, activation='tanh', name='lstm2'))
            model.add(Dropout(0.1))
            model.add(Dense(4))
            model.add(Activation('softmax'))
    
            return model
    
        def train(self, embeddings, x, y):
            model = self.build_network(embeddings, "embeddings_train")
            model.compile(optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"])
    
            # 训练,采用k-folder交叉训练
            for i in range(0, self.max_epochs):
                x_train, x_val, y_train, y_val = self.split_train_data(x, y)
                model.fit(x_train, y_train, batch_size=self.batch_size, validation_data=(x_val, y_val))
    
            # 保存model
            yaml_string = model.to_yaml()
            with open(self.yaml_name, 'w') as outfile:
                outfile.write(yaml.dump(yaml_string, default_flow_style=True))
    
            # 保存model的weights
            model.save_weights(self.model_name)
    
    
        def predict(self, embeddings, x):
            # 加载model
            print 'loading model......'
            with open(self.yaml_name, 'r') as f:
                yaml_string = yaml.load(f)
            model = model_from_yaml(yaml_string)
    
            # 加载权重
            print 'loading weights......'
            model.load_weights(self.model_name, by_name=True)
            model.compile(optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"])
    
            # 预测
            x = sequence.pad_sequences(x, self.max_seq_length)
            predicts = model.predict_classes(x)         # 得到分类结果,它表征的是类别序号
    
            # 转换
            classes = [0, 1, -2, -1]
            predicts = [classes[item] for item in predicts]
            np.set_printoptions(threshold=np.nan)       # 全部打印
            print(np.array(predicts))
    
            return predicts
    

    3.2.4 softmax输出类别

    这一部分上面代码已经讲到了,不在赘述。softmax只是一个归一化,讲数据归一化到[0, 1]之间,从而可以得到每个类别的概率。我们最终取概率最大的即可。

    3.3 基于深度学习的情感分析难点

    基于深度学习的情感分析难点也很多

    1. 语句长度太长。很多用户评论都特别长,分词完后也有几百个词语。而对于LSTM,序列过长会导致计算复杂、精度降低等问题。一般解决方法有进行停用词处理,无关词处理等,从而缩减文本长度。或者对文本进行摘要,抽离出语句主要成分。
    2. 新词和口语化的词语特别多。用户评论语句不像新闻那样规整,新词和口语化的词语特别多。这个问题给分词和词向量带来了很大难度。一般解决方法是分词方面,建立用户词典,从而提高分词准确度。词向量方面,对新词进行增量训练,从而提高新词覆盖率。

    4. 总结

    文本情感分析是NLP领域一个十分重要的问题,对理解用户意图具有决定性的作用。通过基于词典的传统算法和基于深度学习的算法,可以有效的进行情感分析。当前情感分析准确率还有待提高,任重而道远!

    系列文章,欢迎阅读
    NLP预训练模型1 – 综述
    NLP预训练模型2 – BERT详解和源码分析
    Transformer家族1 – Transformer详解和源码分析

    自然语言处理1 – 分词
    自然语言处理2 – jieba分词用法及原理
    自然语言处理3 – 词性标注
    自然语言处理4 – 句法分析
    自然语言处理5 – 词向量
    自然语言处理6 – 情感分析

    展开全文
  • 基于Python的情感分析案例

    万次阅读 多人点赞 2018-01-09 12:55:57
    情感分析:又称为倾向性分析和意见挖掘,它是对带有情感色彩的主观性文本进行分析、处理、归纳和推理的过程,其中情感分析还可以细分为情感极性(倾向)分析,情感程度分析,主客观分析等。 情感极性分析的目的是对...
  • 情感分析发展

    千次阅读 2018-07-14 00:19:42
    哈尔滨工业大学刘挺教授在第三届中国人工智能大会上对自然语言处理的发展趋势做了一次精彩的归纳。趋势 1:语义表示——从符号表示到分布表示自然语言处理一直以来都是比较抽象的,都是直接用词汇和符号来表达概念。...
  • 情感分析 综述

    千次阅读 2019-05-13 16:14:55
    情感分析或观点挖掘是对人们对产品、服务、组织、个人、问题、事件、话题及其属性的观点、情感、情绪、评价和态度的计算研究。该领域的开始和快速发展与社交媒体的发展相一致,如评论、论坛、博客、微博、推特和社交...
  • 情感分析:几乎包括你需要知道的所有(一)

    万次阅读 多人点赞 2019-08-06 09:20:41
    1.情感分析教程: 有一份几乎为所有人准备的情感分析教程,包括程序员,非程序员,营销人员,数据分析师,代理人,销售人员等等。 在本节中,我们将分享各种各样的教程,以便您可以找到适合自己的情绪分析。 1.1 ...
  • 情感分析概述

    千次阅读 2018-06-19 00:34:20
    情感分析主要研究观点挖掘、倾向性分析等。 一,为什么需要观点挖掘和倾向性分析 文本信息主要包括两类: 客观性事实 主观性观点 但是已有的文本分析方法主要侧重在客观性文本内容的分析和挖掘。 二,什么...
  • NLP:文本情感分析快速入门

    万次阅读 多人点赞 2018-08-03 17:59:47
    最近由中国计算机学会(CCF)主办,雷锋网,香港中文大学(深圳)承办的2018全球人工智能与机器人峰会(CCF-GAIR)在深圳召开,由于博主主要做NLP方向,主要在做情感分析,文本生成方面,所以主要关注大会NLP方面的...
  • 本文以数据分析的角度详细介绍情感分析,主要内容如下:情感分析概述、情感分析方法、情感分析工具。
  • 短文本情感分析

    万次阅读 2017-01-03 18:13:08
    一、什么是情感分析情感分析(SA)又称为倾向性分析和意见挖掘,它是对带有情感色彩的主观性文本进行分析、处理、归纳和推理的过程,其中情感分析还可以细分为情感极性(倾向)分析,情感程度分析,主客观分析等。...
  • 中文文本情感分析:基于机器学习方法的思路

    万次阅读 多人点赞 2017-06-14 11:30:56
    1)这是相对于英文文本情感分析,中文独有的预处理。 2)常用方法:基于词典、基于规则、基于统计、基于字标注、基于人工智能。 3)常用工具:哈工大—语言云、东北大学NiuTrans统计机器翻译系统、中科院张华平博士...
  • 中文文章情感分析-开源工具分享

    千次阅读 2017-03-16 14:01:12
    提供一个开源的中文情感分析工具
  • 中文情感分析语料库

    万次阅读 2017-07-19 10:32:33
    中文情感分析语料库
  • snownlp文本情感分析使用

    万次阅读 2017-01-16 14:09:08
    利用snownlp可以进行分词、词性标注、文本摘要提取、文本情感分析等,下面贴出snownlp分词、词性标注、情感分析代码如下: from snownlp import SnowNLP s = SnowNLP(u'这个东西真心很赞') key=s.words #
  • 文本情感分析综述

    万次阅读 2018-02-28 18:33:17
    摘要:文本情感分析:又称意见挖掘、倾向性分析等。简单而言,是对带有情感色彩的主观性文本进行分析、处理、归纳和推理的过程。互联网(如博客和论坛以及社会服务网络如大众点评)上产生了大量的用户参与的、对于诸如...
  • 求助大神!ROST EA情感分析软件情感词典如何更新

    千次阅读 热门讨论 2018-05-04 13:29:10
    我需要对ROST EA软件中的情感词典进行更新,所以需要了解ROST EA中情感计算规则和词汇记忆权重,但是我打开ROST EA情感分析软件中的词库文件是乱码的。请问这该如何处理呢??文件夹中有两个软件,manage是用于新增...
  • 基于文本的情感分析总结

    万次阅读 2018-05-29 21:11:35
    本文主要对网上能搜索到的、现有的基于文本的情感分析方法进行总结和归纳。文本会比较零碎。在下一篇文章中,将对零散知识进行整理。最终应包含4个内容: 什么是基于文本的情感分析 常用词典和数据库 自然语言处理...
1 2 3 4 5 ... 20
收藏数 44,447
精华内容 17,778
关键字:

情感分析