Paddlehub+wechaty实现一个智能微信机器人-好友友好账户管家

前言

本项目是AI ChatBot创意赛的参赛项目

比赛官方地址:https://aistudio.baidu.com/aistudio/competition/detail/79

项目展示

1. 比赛重点难点剖析

  • 比赛要求同时使用paddlehub和wechaty,有的小伙伴可能两个都不熟悉

  • 项目本质上是使用wechaty构建一个ChatBot

  • 在此基础上接入paddlehub可以使我们的ChatBot变得更智能

  • 当然,最重要的是创意,创意,创意!!

2. 项目思路介绍

项目的产生背景

  • 现代人的好友数量众多

  • 仅凭我们自己可能无法判别哪些好友是友好的,哪些是不友好的

  • 有没有一种方式可以方便我们快速的判断

关于好友友好账户的解释

  • 好友友好账户里存的是每一个好友的友好分数

  • 好友发送的消息越友好,友好分数越高

  • 好友发送的消息次数多,友好分数越高

3. 运行环境的配置

可以去看我另一个项目:教你用AI Studio+wechaty+阿里云白嫖一个智能微信机器人

4. 项目的大概流程讲解

  • 机器人应该先识别出主人,并初始化好友列表以及好友友好账户

  • 判断机器人收到的消息是来自主人还是好友

  • 机器人收到主人的特定消息,执行特定功能

  • 机器人收到好友发送的文本消息之后调用paddlehub模型分析友好分数,并记录在好友友好账户里

我使用的模型是ernie_skep_sentiment_analysis
模型地址:https://www.paddlepaddle.org.cn/hubdetail?name=ernie_skep_sentiment_analysis&en_category=SentimentAnalysis

5. 上代码

5.1 脚本文件run.sh

pip install --upgrade pip
pip install wechaty==0.7dev17

# 下载模型
hub install ernie_skep_sentiment_analysis==1.0.0

# 设置环境变量
export WECHATY_PUPPET=wechaty-puppet-service
export WECHATY_PUPPET_SERVICE_TOKEN=puppet_padlocal_xxxxxx    # 填写你自己的token

# 设置使用GPU进行模型预测
export CUDA_VISIBLE_DEVICES=0

python run.py

5.2 python文件run.py

import re
import asyncio
import logging
import os
import random
import paddlehub as hub
from tkinter.constants import NO
from typing import List, Optional, Union
from wechaty_grpc.wechaty.puppet import ContactType

from wechaty_puppet import FileBox, ScanStatus  # type: ignore

from wechaty import Wechaty, Contact
from wechaty.user import Message, Room, contact

logging.basicConfig(level=logging.INFO)
log = logging.getLogger(__name__)

# 定义模型
modle = hub.Module(name="ernie_skep_sentiment_analysis")

def analyse_kind(text, local=False):
    '''
    分析好友所发消息的友好分数
    text: 输入文本
    local: 是否在本地运行
    '''
    if local:
        # 在本地测试,不调用模型
        score_kind = random.randint(-5, 5)
        return score_kind

    else:
        # 转换文本为列表形式
        text = [text]

        # 模型输出结果
        results = modle.predict_sentiment(text, use_gpu=True)
        result = results[0]

        # 如果消息是友好的
        if result['sentiment_label'] == 'positive':
            score_kind = result['positive_probs'] * 5
            score_kind = round(score_kind, 2)

        # 如果消息是不友好的
        elif result['sentiment_label'] == 'negative':
            score_kind = result['negative_probs'] * (-5)
            score_kind = round(score_kind, 2)

        return score_kind


class MyBot(Wechaty):
    """
    listen wechaty event with inherited functions, which is more friendly for
    oop developer
    """
    def __init__(self):
        super().__init__()
        # 主人的contact
        self.host_contact = None

        # 好友的contact
        self.contacts = None

        # 给主人打招呼
        self.say_hello = '你好我的主人,我是主人的好友友好账户管家,主要的功能有: \n1. 管理主人好友的友好账户\n2. 查询主人好友的友好账户\n主人回复相应数字即可查看详细功能'

        # 好友的友好账户
        self.account_kind = {}

    async def on_message(self, msg: Message):
        """
        listen for message event
        """
        from_contact = msg.talker()
        text = msg.text()
        room = msg.room()

        # 识别主人
        if text == '你好我的机器人' and self.host_contact is None:
            # 记录主人的contact
            self.host_contact = from_contact

            # 列举所有好友的contact
            contacts: List[Contact] = await self.Contact.find_all()

            # 过滤一些contact
            self.contacts = [contact for contact in contacts if len(contact.contact_id) > 50]

            # 移除主人的contact
            self.contacts.remove(self.host_contact)

            # 初始化好友的友好账户
            self.account_kind = {contact: 0 for contact in self.contacts}

            # 给主人发消息
            conversation = self.host_contact
            await conversation.ready()
            await conversation.say(self.say_hello)
            # 打印好友的contact
            # for contact in self.contacts:
            #     print(contact.name, contact.payload.alias)
        
        # 如果是主人的消息
        if from_contact == self.host_contact:

            conversation = self.host_contact
            await conversation.ready()

            if text == 'ding':
                await conversation.say('dong')

            if text == '1':
                await conversation.say('当有好友给主人发消息时,将自动分析这句话的友好分数,并记录在主人的好友友好账户上')

            if text == '2':
                await conversation.say('主人按照以下格式即可查询好友的友好账户\n查询 好友 张三\n\n主人按照以下格式即可查询好友友好账户排名前五\n查询 前5\n\n主人按照以下格式即可查询好友友好账户排名后五\n查询 后5')

            if '查询 好友' in text:
                # 提取好友备注或昵称
                friend_name = text.split(' ')[-1]
                # 遍历字典,找打好友的友好账户分数并返回
                for contact, score in list(self.account_kind.items()):
                    if friend_name == contact.name or friend_name == contact.payload.alias:
                        friend_score_kind = round(score, 2)
                        break

                await conversation.say(f'亲爱的主人,{friend_name}目前的友好分数为:{friend_score_kind}')

            if '查询 前' in text:
                # 提取数字
                number = re.findall(r'\d+', text)
                number = int(number[0])

                # 按照账户分数大小给字典排序
                sorted_account_kind = {contact: score for contact, score in sorted(self.account_kind.items(), key=lambda item: item[1], reverse=True)}

                # 给主人发的消息内容
                msg_to_host = f'亲爱的主人,目前好友友好账户排名前{number}的是:'
                for contact, score in list(sorted_account_kind.items())[:number]:
                    # 获取好友备注或昵称
                    friend_name = contact.payload.alias if contact.payload.alias != '' else contact.name
                    msg_to_host += f'\n{friend_name}{round(score, 2)}'

                await conversation.say(msg_to_host)


            if '查询 后' in text:
                # 提取数字
                number = re.findall(r'\d+', text)
                number = int(number[0])
                sorted_account_kind = {contact: score for contact, score in sorted(self.account_kind.items(), key=lambda item: item[1])}

                # 给主人发的消息内容
                msg_to_host = f'亲爱的主人,目前好友友好账户排名后{number}的是:'
                for contact, score in list(sorted_account_kind.items())[:number]:
                    # 获取好友备注或昵称
                    friend_name = contact.payload.alias if contact.payload.alias != '' else contact.name
                    msg_to_host += f'\n{friend_name}{round(score, 2)}'

                await conversation.say(msg_to_host)

        # 好友的消息
        if from_contact in self.contacts and room is None and msg.type() == Message.Type.MESSAGE_TYPE_TEXT:
            # 计算好友消息的友好分数
            msg_score_kind = analyse_kind(text, local=False)

            # 更新好友的友好账户
            self.account_kind[from_contact] += msg_score_kind
            self.account_kind[from_contact] = round(self.account_kind[from_contact], 2)

            # 获取好友备注或昵称
            friend_name = from_contact.payload.alias if from_contact.payload.alias != '' else from_contact.name

            # 给主人汇报消息
            conversation = self.host_contact
            await conversation.ready()
            await conversation.say(f'亲爱的主人,{friend_name}给您发了一条消息\n消息内容是: {text}\n友好分数是: {msg_score_kind}\n{from_contact.name}目前的友好分数为:{self.account_kind[from_contact]}')

    async def on_login(self, contact: Contact):
        print(f'user: {contact} has login')

    async def on_scan(self, status: ScanStatus, qr_code: Optional[str] = None,
                      data: Optional[str] = None):
        contact = self.Contact.load(self.contact_id)
        print(f'user <{contact}> scan status: {status.name} , '
              f'qr_code: {qr_code}')


bot: Optional[MyBot] = None


async def main():
    """doc"""
    # pylint: disable=W0603
    global bot
    bot = MyBot()
    await bot.start()


asyncio.run(main())

6. 项目完善(使用多个模型协同进行文本分析)

  • 关于情感分析模型的对比,可以看我的另一个项目: 情感分析模型的对比

  • 分析可知模型ernie_skep_sentiment_analysis的准确率较高

  • 模型ernie_skep_sentiment_analysis对于一些具有明显感情色彩的文本给出的置信度并不是很高

  • 但其他几个模型能给出较高的置信度

  • 于是我们可以使用ernie_skep_sentiment_analysis模型作为主模型,判断情感色彩是正面还是负面

  • 使用其他几个模型作为辅助模型,当情感色彩与主模型一致时,分数记录下来,最后取平均

代码如下

from numpy import mean

# 定义主模型
modle_main = hub.Module(name="ernie_skep_sentiment_analysis")

# 定义辅助模型
model_lstm = hub.Module(name="senta_lstm")
model_bilstm = hub.Module(name="senta_bilstm")
model_gru = hub.Module(name="senta_gru")

def analyse_kind(text):
    # 转换文本为列表形式
    text = [text]

    # 主模型输出结果
    results_main = modle_main.predict_sentiment(text, use_gpu=True)
    result_main = results_main[0]

    # 辅助模型输出结果
    results_lstm = model_lstm.sentiment_classify(text, use_gpu=True)
    result_lstm = results_lstm[0]

    results_bilstm = model_bilstm.sentiment_classify(text, use_gpu=True)
    result_bilstm = results_bilstm[0]

    results_gru = model_gru.sentiment_classify(text, use_gpu=True)
    result_gru = results_gru[0]

    results = [result_lstm, result_bilstm, result_gru]

    list_score = []

    # 如果消息是友好的
    if result_main['sentiment_label'] == 'positive':
        score = result_main['positive_probs'] * 5
        list_score.append(score)
        for result in results:
            if result['sentiment_label'] == 'positive':
                score = result['positive_probs'] * 5
                list_score.append(score)


    # 如果消息是不友好的
    elif result_main['sentiment_label'] == 'negative':
        score = result_main['negative_probs'] * (-5)
        list_score.append(score)
        for result in results:
            if result['sentiment_label'] == 'negative':
                score = result['negative_probs'] * (-5)
                list_score.append(score)

    score = mean(list_score)
    score = round(score, 2)

core.append(score)
        for result in results:
            if result['sentiment_label'] == 'negative':
                score = result['negative_probs'] * (-5)
                list_score.append(score)

    score = mean(list_score)
    score = round(score, 2)

    return score

后记

机器人还不太完善的地方

  • 如果消息中有表情,不能准确判断友好分数

  • 对于少部分消息内容不能准确判断友好分数

改进完善方向

同时使用多个paddlehub模型协同进行预测可能效果会更好(已完善)

更多功能

飞桨使用体验

飞桨PaddlePaddle是百度自研的开源深度学习平台,有全面的官方支持的工业级应用模型,涵盖自然语言处理、计算机视觉、推荐引擎等多个领域,拥有EasyDL、AI Studio等服务平台,并为开发者开放PaddleHub、PaddleX、VisualDL等一系列深度学习工具组件,更有PaddleNLP、PaddleCV、PaddleRec等基础模型库,帮助开发者快速落地AI应用。总之,飞桨PaddlePaddle简直就是宝藏!!

参考资料

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐