「GPT虚拟直播」实战篇|GPT接入虚拟人实现直播间弹幕回复

摘要

ChatGPT和元宇宙都是当前数字化领域中非常热门的技术和应用。结合两者的优势和特点,可以探索出更多的应用场景和商业模式。例如,在元宇宙中使用ChatGPT进行自然语言交互,可以为用户提供更加智能化、个性化的服务和支持;在ChatGPT中使用元宇宙进行虚拟现实体验,可以为用户提供更加真实、丰富、多样化的交互体验。
下面我将结合元宇宙和ChatGPT的优势,实战开发一个GPT虚拟直播的Demo并推流到抖音平台,

NodeJS接入ChatGPT与即构ZIM

上一篇文章《人人都能用ChatGPT4.0做Avatar虚拟人直播》,主要介绍了如何使用ChatGPT+即构Avatar做虚拟人直播。由于篇幅原因,对代码具体实现部分描述的不够详细。收到不少读者询问代码相关问题,接下来笔者将代码实现部分拆分2部分来详细描述:

  1. NodeJS接入ChatGPT与即构ZIM
  2. ChatGPT与即构Avatar虚拟人对接直播

本文主要讲解如何接入ChatGPT并实现后期能与Avatar对接能力。

在开始讲具体流程之前,我们先来回顾一下整个GPT虚拟直播Demo的实现流程图,本文要分享的内容是下图的右边部分的实现逻辑。

1 基本原理

ChatGPT是纯文本互动,那么如何让它跟Avatar虚拟人联系呢?
首先我们已知一个先验:

  • 即构Avatar有文本驱动能力,即给Avatar输入一段文本,Avatar根据文本渲染口型+播报语音
  • 将观众在直播间发送的弹幕消息抓取后,发送给OpenAI的ChatGPT服务器
  • 得到ChatGPT回复后将回复内容通过Avatar语音播报
    在观众看来,这就是在跟拥有ChatGPT一样智商的虚拟人直播互动了。

2 本文使用的工具

  • GPT虚拟直播弹幕:即构ZIM语聊房群聊弹幕
  • GPT4.0:New bing
  • GPT3.5:ChatGPT

3 对接ChatGPT

这里主要推荐2个库:

  • chatgpt-api
  • chatgpt

chatgpt-api封装了基于bing的chatgpt4.0,chatgpt基于openAI官方的chatgpt3.5。具体如何创建bing账号以及如何获取Cookie值以及如何获取apiKey,可以参考我另一篇文章《人人都能用ChatGPT4.0做Avatar虚拟人直播》。

3.1 chatgpt-api

安装:

npm i @waylaidwanderer/chatgpt-api

bing还没有对中国大陆开放chatgpt,因此需要一个代理,因此需要把代理地址也一起封装。代码如下:


import { BingAIClient } from '@waylaidwanderer/chatgpt-api';

export class BingGPT {
    /*
    * http_proxy, apiKey
    **/
    constructor(http_proxy, userCookie) {
        this.api = this.init(http_proxy, userCookie);
        this.conversationSignature = "";
        this.conversationId = "";
        this.clientId = "";
        this.invocationId = "";
    }
    init(http_proxy, userCookie) {
       console.log(http_proxy, userCookie)
        const options = { 
            host: 'http://www.bing.com', 
            userToken: userCookie,
            // If the above doesn't work, provide all your cookies as a string instead
            cookies: '',
            // A proxy string like "http://<ip>:<port>"
            proxy: http_proxy,
            // (Optional) Set to true to enable `console.debug()` logging
            debug: false,
        };

        return new BingAIClient(options);
    }
    //
    //此处省略chat函数......
    //
} 

上面代码完成了VPN和BingAIClient的封装,还缺少聊天接口,因此添加chat函数完成聊天功能:

//调用chatpgt 
chat(text, cb) {
    var res=""
    var that = this;
    console.log("正在向bing发送提问", text ) 
    this.api.sendMessage(text, { 
        toneStyle: 'balanced',
        onProgress: (token) => { 
            if(token.length==2 && token.charCodeAt(0)==55357&&token.charCodeAt(1)==56842){
                cb(true, res);
            } 
            res+=token;
        }
    }).then(function(response){ 
        that.conversationSignature = response.conversationSignature;
        that.conversationId = response.conversationId;
        that.clientId = response.clientId;
        that.invocationId = response.invocationId;
    }) ;  

}

在使用的时候只需如下调用:

var bing = new BingGPT(HTTP_PROXY, BING_USER_COOKIE);
bing.chat("这里传入提问内容XXXX?", function(succ, response){
    if(succ)
        console.log("回复内容:", response)
})

需要注意的是,基于bing的chatgpt4.0主要是通过模拟浏览器方式封住。在浏览器端有很多防机器人检测,因此容易被卡断。这里笔者建议仅限自己体验,不适合作为产品接口使用。如果需要封装成产品,建议使用下一节2.2内容。

3.2 chatgpt

安装:

npm install chatgpt

跟上一小节2.1类似,基于openAI的chatgpt3.5依旧需要梯子才能使用。chatgpt库没有内置代理能力,因此我们可以自己安装代理库:

npm install http-proxy-agent node-fetch

接下来将代理和chatgpt库一起集成封装成一个类:

import { ChatGPTAPI } from "chatgpt";
import proxy from "http-proxy-agent";
import nodeFetch from "node-fetch";

export class ChatGPT {
  
    constructor(http_proxy, apiKey) {
        this.api = this.init(http_proxy, apiKey);
        this.conversationId = null;
        this.ParentMessageId = null;
    }
    init(http_proxy, apiKey) {
        console.log(http_proxy, apiKey)
        return new ChatGPTAPI({
            apiKey: apiKey,
            fetch: (url, options = {}) => {
                const defaultOptions = {
                    agent: proxy(http_proxy),
                };

                const mergedOptions = {
                    ...defaultOptions,
                    ...options,
                };

                return nodeFetch(url, mergedOptions);
            },
        });
    }
    //...
    //此处省略chat函数
    //...
} 

完成ChatGPTAPI的封装后,接下来添加聊天接口:

//调用chatpgt 
chat(text, cb) {
    let that = this
    console.log("正在向ChatGPT发送提问:", text)
    that.api.sendMessage(text, {
        conversationId: that.ConversationId,
        parentMessageId: that.ParentMessageId
    }).then(
        function (res) {
            that.ConversationId = res.conversationId
            that.ParentMessageId = res.id
            cb && cb(true, res.text)
        }
    ).catch(function (err) {
        console.log(err)
        cb && cb(false, err);
    });
}

使用时就非常简单:

var chatgpt =  new ChatGPT(HTTP_PROXY, API_KEY);
chatgpt.chat("这里传入提问内容XXXX?", function(succ, response){
    if(succ)
        console.log("回复内容:", response)
})

chatgpt库主要基于openAI的官方接口,相对来说比较稳定,推荐这种方式使用。

3.3 两库一起封装

为了更加灵活方便使用,随意切换chatgpt3.5和chatgpt4.0。将以上两个库封装到一个接口中。

首先创建一个文件保存各种配置, KeyCenter.js:

const HTTP_PROXY = "http://127.0.0.1:xxxx";//本地vpn代理端口
//openAI的key, 
const API_KEY = "sk-xxxxxxxxxxxxxxxxxxxxxxxxx";
//bing cookie
const BING_USER_COOKIE = 'xxxxxxxxxxxxxxxxxxxxxxxx--BA';

module.exports = { 
    HTTP_PROXY: HTTP_PROXY,
    API_KEY: API_KEY,
    BING_USER_COOKIE:BING_USER_COOKIE
}

注意,以上相关配置内容需要读者替换。

接下来封装两个不同版本的chatGPT:

const KEY_CENTER = require("../KeyCenter.js");
var ChatGPTObj = null, BingGPTObj = null;
//初始化chatgpt
function getChatGPT(onInitedCb) {
    if (ChatGPTObj != null) {
        onInitedCb(true, ChatGPTObj);
        return;
    }
    (async () => {
        let { ChatGPT } = await import("./chatgpt.mjs");
        return new ChatGPT(KEY_CENTER.HTTP_PROXY, KEY_CENTER.API_KEY);
    })().then(function (obj) {
        ChatGPTObj = obj;
        onInitedCb(true, obj);
    }).catch(function (err) {
        onInitedCb(false, err);
    });
}

function getBingGPT(onInitedCb){
    if(BingGPTObj!=null) {
        onInitedCb(true, BingGPTObj);
        return;
    }
    (async () => {
        let { BingGPT } = await import("./binggpt.mjs");
        return new BingGPT(KEY_CENTER.HTTP_PROXY, KEY_CENTER.BING_USER_COOKIE);
    })().then(function (obj) {
        BingGPTObj = obj;
        onInitedCb(true, obj);
    }).catch(function (err) {
        console.log(err)
        onInitedCb(false, err);
    });
}

上面两个函数getBingGPTgetChatGPT分别对应2.1节2.2节封装的版本。在切换版本的时候直接调用对应的函数即可,但笔者认为,还不够优雅!使用起来还是不够舒服,因为需要维护不同的对象。最好能进一步封装,调用的时候一行代码来使用是最好的。那进一步封装,补充以下代码:

//调用chatgpt聊天
function chatGPT(text, cb) {
    getChatGPT(function (succ, obj) {
        if (succ) {
            obj.chat(text, cb);
        } else {
            cb && cb(false, "chatgpt not inited!!!");
        }
    })
}

function chatBing(text, cb){
    getBingGPT(function (succ, obj) {
        if (succ) {
            obj.chat(text, cb);
        } else {
            cb && cb(false, "chatgpt not inited!!!");
        }
    })

}

module.exports = {
    chatGPT: chatGPT,
    chatBing:chatBing
} 

加了以上代码后,就舒服多了:想要使用bing的chatgpt4.0,那就调用chatBing函数好了;想要使用openAI官方的chatgpt3.5,那就调用chatGPT函数就好!

4 对接Avatar

4.1 基本思路

好了,第2节介绍了对chatgpt的封装,不同的版本只需调用不同函数即可实现与chatgpt对话。接下来怎么将chatGPT的文本对话内容传递给Avatar呢?即构Avatar是即构推出的一款虚拟形象产品,它可以跟即构内的其他产品对接,比如即时通讯ZIM和音视频通话RTC。这就好办了,我们只需利用ZIM或RTC即可。

这里我们主要利用即构ZIM实现,因为即构ZIM非常方便实时文本内容。即构ZIM群聊消息稳定可靠,延迟低,全球任何一个地区都有接入服务的节点保障到达。

尤其是ZIM群聊有弹幕功能,相比发送聊天消息,发送弹幕消息不会被存储,更适合直播间评论功能。

4.2 代码实现

即构官方提供的js版本库主要是基于浏览器,需要使用到浏览器的特性如DOM、localStorage等。而这里我们主要基于NodeJS,没有浏览器环境。因此我们需要安装一些必要的库, 相关库已经在package.json有记录,直接执行如下命令即可:

npm install

4.2.1 创建模拟浏览器环境

首先执行浏览器环境模拟,通过fake-indexeddb、jsdom、node-localstorage库模拟浏览器环境以及本地存储环境。创建WebSocket、XMLHttpRequest等全局对象。

var fs = require('fs');
//先清理缓存
fs.readdirSync('./local_storage').forEach(function (fileName) {
    fs.unlinkSync('./local_storage/' + fileName);
});

const KEY_CENTER = require("../KeyCenter.js");
const APPID = KEY_CENTER.APPID, SERVER_SECRET = KEY_CENTER.SERVER_SECRET;
const generateToken04 = require('./TokenUtils.js').generateToken04;
var LocalStorage = require('node-localstorage').LocalStorage;
localStorage = new LocalStorage('./local_storage');
var indexedDB = require("fake-indexeddb/auto").indexedDB;
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const dom = new JSDOM(``, {
    url: "http://localhost/",
    referrer: "http://localhost/",
    contentType: "text/html",
    includeNodeLocations: true,
    storageQuota: 10000000
});
window = dom.window;
document = window.document;
navigator = window.navigator;
location = window.location;
WebSocket = window.WebSocket;
XMLHttpRequest = window.XMLHttpRequest;

4.2.2 创建ZIM对象

将即构官方下载的index.js引入,获取ZIM类并实例化,这个过程封装到createZIM函数中。需要注意的是登录需要Token,为了安全考虑,Token建议在服务器端生成。接下来把整个初始化过程封装到initZego函数中,包含注册监听接收消息,监控Token过期并重置。

const ZIM = require('./index.js').ZIM; 

function newToken(userId) {
    const token = generateToken04(APPID, userId, SERVER_SECRET, 60 * 60 * 24, '');
    return token;
}
/**
 * 创建ZIM对象
*/
function createZIM(onError, onRcvMsg, onTokenWillExpire) {
    var zim = ZIM.create(APPID);
    zim.on('error', onError);
    zim.on('receivePeerMessage', function (zim, msgObj) {
        console.log("收到P2P消息")
        onRcvMsg(false, zim, msgObj)
    });
    // 收到群组消息的回调
    zim.on('receiveRoomMessage', function (zim, msgObj) {
        console.log("收到群组消息")
        onRcvMsg(true, zim, msgObj)
    });

    zim.on('tokenWillExpire', onTokenWillExpire);

    return zim;
}
/*
*初始化即构ZIM
*/
function initZego(onError, onRcvMsg, myUID) {
    var token = newToken(myUID);
    var startTimestamp = new Date().getTime();
    function _onError(zim, err) {
        onError(err);
    }
    function _onRcvMsg(isFromGroup, zim, msgObj) {
        var msgList = msgObj.messageList;
        var fromConversationID = msgObj.fromConversationID;
        msgList.forEach(function (msg) {
            if (msg.timestamp - startTimestamp >= 0) { //过滤掉离线消息
                var out = parseMsg(zim, isFromGroup, msg.message, fromConversationID)
                if (out)
                    onRcvMsg(out); 
            }
        })

    }
    function onTokenWillExpire(zim, second) {
        token = newToken(userId);
        zim.renewToken(token);
    }
    var zim = createZIM(_onError, _onRcvMsg, onTokenWillExpire);
    login(zim, myUID, token, function (succ, data) {
        if (succ) {
            console.log("登录成功!")

        } else {
            console.log("登录失败!", data)
        }
    })
    return zim;
}

4.2.3 登录、创建房间、加入房间、离开房间

调用zim对象的login函数完成登录,封装到login函数中;调用zim对象的joinRoom完成加入房间,封装到joinRoom函数中;调用zim的leaveRoom函数完成退出房间,封装到leaveRoom函数中。

/**
 * 登录即构ZIM
*/
function login(zim, userId, token, cb) {
    var userInfo = { userID: userId, userName: userId };

    zim.login(userInfo, token)
        .then(function () {
            cb(true, null);
        })
        .catch(function (err) {
            cb(false, err);
        });
}
/**
 * 加入房间
*/
function joinRoom(zim, roomId, cb = null) {
    zim.joinRoom(roomId)
        .then(function ({ roomInfo }) {

            cb && cb(true, roomInfo);
        })
        .catch(function (err) {
            cb && cb(false, err);
        });
}
/**
 * 离开房间
*/
function leaveRoom(zim, roomId) {

    zim.leaveRoom(roomId)
        .then(function ({ roomID }) {
            // 操作成功
            console.log("已离开房间", roomID)
        })
        .catch(function (err) {
            // 操作失败
            console.log("离开房间失败", err)
        });
}

4.2.4 发送消息、解析消息

发送消息分为一对一发送和发送到房间,这里通过isGroup参数来控制,如下sendMsg函数所示。将接收消息UID和发送内容作为sendMsg参数,最终封装并调用ZIM的sendMessage函数完成消息发送。

接收到消息后,在我们的应用中设置了发送的消息内容是个json对象,因此需要对内容进行解析,具体的json格式可以参考完整源码,这里不做详细讲解。

/**
 * 发送消息
*/
function sendMsg(zim, isGroup, msg, toUID, cb) { 
    var type = isGroup ? 1 : 0; // 会话类型,取值为 单聊:0,房间:1,群组:2
    var config = {
        priority: 1, // 设置消息优先级,取值为 低:1(默认),中:2,高:3
    }; 
    var messageTextObj = { type: 20, message: msg, extendedData: '' };
    var notification = {
        onMessageAttached: function (message) { 
            console.log("已发送", message)
        }
    } 
    zim.sendMessage(messageTextObj, toUID, type, config, notification)
        .then(function ({ message }) {
            // 发送成功
            cb(true, null);
        })
        .catch(function (err) {
            // 发送失败
            cb(false, err)
        }); 
}
/**
 * 解析收到的消息
*/
function parseMsg(zim, isFromGroup, msg, fromUid) {
    //具体实现略
}

4.2.5 导出接口

有了以上的实现后,把关键函数导出暴露给其他业务调用:

module.exports = {
    initZego: initZego,
    sendMsg: sendMsg,
    joinRoom: joinRoom
}

以上代码主要封装:

  1. 即构ZIM初始化
  2. 发送消息
  3. 加入房间

至此,我们就具备了将chatgpt消息群发到一个房间的能力、加入房间、接收到房间的弹幕消息能力。

更多关于即构ZIM接口与官方Demo可以点击参考这里,对即构ZIM了解更多可以点击这里

关于Avatar如何播报chatgpt内容,我们在下一篇文章实现。

5 相关代码

  1. nodejs接入chatgpt与即构zim
本文转载于网络 如有侵权请联系删除

相关文章

  • 在动态场景中学习时空占位网格图以实现终生导航(cs.RO)

    我们提出了一种新的方法来生成、预测和使用时空占位网格图(SOGM),它嵌入了动态场景的未来信息。我们的自动生成过程从以前的导航数据中创建了真实的时空占位网格图。我们用它们来训练一个3D-2D前馈结构,以自我监督的方式,从而实现机器人的终身学习。自动生成过程使用光线跟踪,根据机器人环境中的动态属性对点进行标记。该网络由一个三维后端和一个二维前端组成,前者提取丰富的特征并实现激光雷达帧的语义分割,后者预测嵌入时空占位网格图的未来信息。我们还设计了一个使用这些预测的时空占位网格图的导航管道。我们对预测提供了定量和定性的见解,并通过一项消融研究验证了我们对网络设计的选择。原文题目:LearningSpatiotemporalOccupancyGridMapsforLifelongNavigationinDynamicScenes原文:Wepresentanovelmethodforgenerating,predicting,andusingSpatiotemporalOccupancyGridMaps(SOGM),whichembedfutureinformationofdynamicscen

  • SAP S4HANA 2020 Fully-Activated Appliance 虚拟机版分享

    花费了整整一个周末加两个晚上,终于将最新的SAPS/4HANA2020,Fully-ActivatedAppliance从Amazon远程主机打包下载下来,做成VM虚拟机,对,你没看错,很多人心心念念的Fully-ActivatedAppliance版本,自带业务数据,简称FAA。整个过程非常的艰辛,不仅需要在Amazon上提交工单增加配额,同时还要配置各种参数,主机上打包系统花了5-6个小时,中间还断线了一次,还得从来。因为国内的网络不给力,下载速度太慢,所以采用加拿大的服务器从Amazon主机ftp到本地,然后再传到网盘上,下载过程也很辛苦,足足花了二十多个小时,生怕掉线又得重新来过。但上传网盘的时候又差点崩溃,百度网盘在加拿大海外不给力,上传只有200-300KB/S,尝试了很多的网盘,最终挑选AppleiCloud作为中间云盘,购买iCloud容量,先传上去,然后国内这边再下载下来,123G的数据库上传下载足足用了15个小时,分了33个压缩包。因为手头上没有合适的机子安装,只好借用一个朋友的日本远端主机,然后又得在那台服务器上重新下载一遍。(虚拟机系统桌面) 整个系统安装过程非

  • SQL调优和诊断从哪入手?

    出品丨TeacherWhat关键字:Oracle、SQL、调优、诊断、手把手数据库入门、Database正文约2000字,建议阅读时间5分钟 目录结构:1.如何定位SQL问题2.SQL相关的问题类别3.诊断SQL性能问题需要的相关信息4.基本信息5.获取执行计划的主要方法和工具本公众号文章仅代表个人观点,与任何公司无关。 SQL调优和诊断(一)概述本系列文章将介绍OracleSQL调优和诊断的基本方法和相关工具的使用。本文作为概要,包括如何定位SQL问题、SQL相关的问题类别以及诊断SQL性能问题需要的相关信息。如何定位SQL问题我们在解决SQL相关问题时,需要像解决数据库全体性能问题时一样,自底(OS)向上一步一步进行缩小范围(NarrowDown),做到有的放矢。个人非常赞同ChristianAntognini在其TroubleshootingOraclePerformance一书中介绍的定位过程,如下图:▲摘自TroubleshootingOraclePerformance,2ndEditionChristianAntognini一般情况下,定位过程如下:1.首先排除数据库以外的

  • 生物黑客,是怎样一种神秘存在?

    生物黑客,又称生物崩客Biohack自己动手的生物学家,车库生物学家等,为了防止出现技术被少数专业人士所掌握,而形成的垄断操纵,而产生的一群团体。他们主要是通过网络,及其他手段来普及现代生物学知识。生物黑客的目标是把生物技术带出实验室,打破常规实验室的限制,在不同环境下创新发展生物技术。那么→生物黑客,是怎样一种神秘存在?↓埋在大脑内部的植入物,可能为恢复仿生眼睛提供希望。一旦成功完成手术,就可以将由相机捕获的数字图像转换成产生多个光点的电脉冲,从而使盲人“看到”简单的形状。葡萄糖控制的胰岛素输送系统通过外置胰岛素的输送来调节葡萄糖水平,这可能在糖尿病的治疗中具有重要的应用。用于军事用途的“牙齿麦克风”麦克风使用户能够在危险或活动情况下继续通信,例如从飞机上跳伞,在嘈杂的直升机附近工作,在开阔的水中游泳,或在执行救援任务或交火时。由于它隐藏在嘴里,因此安全人员或秘密特工也可以使用。感受“大气”机器人艺术家ManelMuñoz在他的身体中安装了一个大气感觉器官,能通过耳朵中的振动感受大气变化。数以千计的瑞典人把米粒大小的RFID植入手臂中。他们用千元左右的成本,就能用这种设备取代钥匙、密

  • Tensorflow入门教程(四十六)——SDU-Net

    今天将分享Unet的改进模型SDU-Net,改进模型来自2020年的论文《UNetUsingStackedDilatedConvolutionsforMedicalImageSegmentation》,简单明了给大家分析理解该模型思想。1、SDU-Net网络优点UNet体系结构适用于图像分割,部分原因是编码器和解码器之间的跳过连接对于上采样较低分辨率层是至关重要。但是,U-Net架构引入了折衷:(1)U-Net卷积的感受野非常有限,编码器下采样可能会降低像素之间的相关性。为了获得更大的感受野,Devalla等将空洞卷积引入U-Net,其中,在降低分辨率的同时,空洞率增加。然而Hamaguchi等指出,由于核的稀疏性,大幅度提高空洞率可能无法融合局部特征,并可能损害小物体。(2)现有方法基于未明确的假设,即每个编码器/解码器操作在像素处感知并输出单个感受野,这可能会限制感受野的种类。(3)一些医学成像模式,尤其是超声,需要高的时间分辨率,但又要高效地使用计算资源,从而限制了计算密集型网络体系结构的部署。为了充分利用U-Net的优越分割性能,同时缓解诸如简单和小的感受野之类的缺点,文章提出

  • AI大觉醒:图灵奖得主Bengio称AI将产生意识,未来机器学习核心是注意力机制

    新智元报道来源:venturebeat编辑:梦佳【新智元导读】人工智能是时候该觉醒了吗?在本周的2020ICLR大会上,图灵奖得主YoshuaBengio针对AI和机器学习的未来阐述了他的最新见解。他讲到未来机器学习完全有可能超越无意识,向全意识迈进。而注意力机制正是实现这一过程的关键要素。「新智元急聘主笔、高级主任编辑,添加HR微信(Dr-wly)或扫描文末二维码了解详情。」人工智能会产生意识吗?这是一直以来美剧《西部世界》中探讨的问题。AI主人公觉醒,意识到这个世界是人类杀伐主宰的乐园,于是开启了逆袭之路。在本周举行的2020年ICLR上,图灵奖得主、蒙特利尔学习算法研究所主任YoshuaBengio对AI和机器学习的未来提供了最新的见解。他讲到未来机器学习完全有可能超越无意识,向全意识迈进。而注意力机制正是实现这一过程的关键要素。这位大咖2月份刚刚在纽约的2020年AAAI会议上与图灵奖获得者GeoffreyHinton和YannLeCun一起发表了演讲。而在ICLR的演讲中,Bengio阐述了他更早之前的一些想法。注意力机制是啥?注意力机制来源于人类的视觉注意力,是人类在进化过

  • 32分钟训练神经机器翻译,速度提升45倍

    选自Facebook作者:MICHAELAULI、MYLEOTT、SERGEYEDUNOV机器之心编译深度模型的训练时间通常对研究者而言都是很大的挑战,我们需要花数小时甚至数天才能知道某个小改进到底好不好。然而如果我们从一开始就考虑降低模型训练时间,那么很多概念都能迅速验证。在Facebook开发者的试验中,他们采用了低精度和大批量等一系列加速训练方法,并成功地将需要24小时训练的NMT降低到32分钟。该项目相关的分布式训练代码已开源。 项目地址:https://github.com/pytorch/fairseq我们想让用户用自己喜欢的语言体验我们的产品,同时与世界各地的人们建立联系。为此,我们使用神经机器学习(NMT)自动翻译帖子和内容中的文本。我们之前关于这一课题的研究fairseq已经开源,这是一个序列到序列的学习库,可供任何人训练NMT模型,完成自动摘要或其他文本生成任务。随着NMT模型在从大规模单语数据(只有一种语言的数据)中的学习越来越成功,训练速度变得越来越重要。为了适应这样的发展,我们必须想办法大大减少训练时间。直到最近,NMT模型的这种训练在单台机器上还需要数周,对

  • c++ list, vector, map, set 区别与用法比较

    List封装了链表,Vector封装了数组,list和vector得最主要的区别在于vector使用连续内存存储的,他支持[]运算符,而list是以链表形式实现的,不支持[]。Vector对于随机访问的速度很快,但是对于插入尤其是在头部插入元素速度很慢,在尾部插入速度很快。List对于随机访问速度慢得多,因为可能要遍历整个链表才能做到,但是对于插入就快的多了,不需要拷贝和移动数据,只需要改变指针的指向就可以了。另外对于新添加的元素,Vector有一套算法,而List可以任意加入。 Map,Set属于标准关联容器,使用了非常高效的平衡检索二叉树:红黑树,他的插入删除效率比其他序列容器高是因为不需要做内存拷贝和内存移动,而直接替换指向节点的指针即可。 Set和Vector的区别在于Set不包含重复的数据。Set和Map的区别在于Set只含有Key,而Map有一个Key和Key所对应的Value两个元素。 Map和Hash_Map的区别是Hash_Map使用了Hash算法来加快查找过程,但是需要更多的内存来存放这些Hash桶元素,因此可以算得上是采用空间来换取时间策略。1 vector   

  • honeyd蜜罐配置和web监听脚本

    Honeyd的安装和配置      Honeyd软件依赖于以下几个库及arpd工具:     (1)Libevent:是一个非同步事件通知的函数库。通过使用libevent,开发人员可以设定某些事件发生时所执行的函数,可以代替以往程序所使用的循环检查;     (2)Libdnet:是一个提供了跨平台的网络相关API的函数库,包括arp缓存,路由表查询,IP包及物理帧的传输等;    (3)Libpcap:是一个数据包捕获(PacketSniffing)的函数库,大多数网络软件都以它为基础;    (4)Arpd工具:arpd运行在与honeyd相同的系统上,是honeyd众多协作工具中最重要的一个。Arpd工作时监视局域网内的流量,并通过查看honeyd系统的ARP表判断其它系统的活动与否。当一次企图对局域网内系统的连接发生时,Arpd通过查找ARP表得知目的IP地址不存在后,就会尝试对受害者的IP地址进行ARP广播,如果honeyd得到了响应,说明目标系统确实存在,于是把目标系统的IP地址与MAC地址的对应写入honeyd的ARP表,并对这次连接尝试不动作,因为这可能是合法流量。如

  • 【Oracle笔记】创建表空间、用户及给用户授权SQL和模板

    1.规划 表空间名称:yourname 表空间数据文件存放位置:/u01/app/oracle/oradata/orcl/(selectfile_namefromdba_data_files;) 用户名/密码:yourname/yourname 说明:表空间名称、用户名、密码开发时因便于记忆一般一致,默认为项目的名称,也可以不同。 ****************替换下面的yourname**************** 2.创建临时表空间 CREATETEMPORARYTABLESPACE"temp_yourname" TEMPFILE'/u01/app/oracle/oradata/orcl/temp_yourname.dbf' SIZE20M AUTOEXTENDON NEXT32MMAXSIZE2048M EXTENTMANAGEMENTLOCAL;复制注1:一般数据库实例安装完成后默认有临时表空间“TEMP”,如果小型数据库用此临时表空间即可。 注2:临时表空间主要用来做查询和存放一些缓冲区数据,消耗的主要原因是需要对查询的中间结果进行排序。主要作用:索引create/

  • 一个js文件控制的无刷新翻页

    只要一个js文件就可以控制翻页了,结构非常简单,ajax翻页,传递参数的话可以自己修改,也很简单控制 本例数据库与《jQuery+Ajax+PHP+Mysql实现分页显示数据》相同 演示    js文件 JavaScriptCode var http_request=false;    function send_request(url){//初始化,指定处理函数,发送请求的函数    http_request=false;    //开始初始化XMLHttpRequest对象    if(window.XMLHttpRequest){//Mozilla浏览器    http_request=new XMLHttpRequest();    if(http_request.overrideMimeType){//设置MIME类别  

  • 进制转换

    十进制转二进制 转换方法: 整数部分:除2取余,然后翻转 小数部分:乘2取整 例如:将(457.875)10转换成二进制 1.先转整数部分: 得到:110010001 2.再转小数部分: 得到:111 所以(475.875)10=(110010001.111)2 十进制转其他进制就把上面方法中的每一步的2换成对应的进制就可以了。 再介绍一个典型的 二进制转八进制 转换方法:从小数点中间往两边扩散,三个三个来转成八进制,不够补0 例如:将(1100100010.11)2转成八进制 得到(1443.6)8 十六进制转换方法与八进制相似,取的时候换成四个四个取就可以了。

  • [ALM]一步一步搭建MS ALM环境 - 安装TFS + SQL SERVER

    描述:安装SQLSERVER2012,安装TFS2013,配置TFS,挽起袖子,准备干活儿步骤:1,打开Hyper-VManager,参考[Hyper-V]使用操作系统模板创建新的虚拟机,先完成操作系统的安装,注意以下修改以下信息:磁盘名称:tfs.vhdx磁盘存储位置:x:\VMs\DC虚拟机名称:TFS使用现有的虚拟机磁盘:x:\VMs\DC\tfs.vhdx选择使用内网网卡:InternalVirtualNetwork内存大小:4096MB服务器角色:TeamFoundatitonServer,MSSQLServer2,参考[ALM]一步一步搭建MSALM环境-安装域服务器这篇文字,设置服务器网络、名称、加入域服务器域\名称:ALM\TFS服务器IP:192.168.35.6网关IP:192.168.35.1加入域:ALM3,首先安装MSSQL,其次TFS,先来看MSSQL,如果不喊停,一路Next、OK下去 停!勾选同意服务条款,然后继续狂奔 按照下图勾选SQLFeatures 首先选择混合登录模式,其次输入sa密码Admin@1234!为当前用户

  • 备战机试

    http://ac.jobdu.com/problem.php?pid=1547 卡特兰数 11251442132429...... 公式h[0]=1,h[1]=1,h[2]=2; h[n]=h[n-1]*h[0]+h[n-2]*h[1]+...+h[0]*h[n-1];  O(n^2)   h[n]=h[n-1]*(4*n-2)/(n+1) 除法逆元   h(n)=C(2n,n)/(n+1)(n=0,1,2,...) 组合数   百度百科:http://baike.baidu.com/view/2499752.htm?fr=aladdin

  • 我的js笔记

    一、鼠标的绝对位置和相对位置       注:需要引入JQ          <!DOCTYPEhtml>   <html>   <head>     <scripttype="text/javascript"src="jquery-3.0.0.js"></script>     <scripttype="text/javascript">             functionclick1(event){           vardata1=mouseCoords(event);          &nb

  • ros_qt工程打包deb格式

    ros_qt工程打包deb格式 介绍 ros_qt工程完成后,经常需要部署在其他电脑,配置环境非常繁琐,将工程的可执行文件打包为deb格式,即可以直接安装运行。以下以工程record_bag为例。 文件目录结构 步骤 新建文件夹如文件目录结构图,将ros工程编译好的可执行文件(record_bag:工作空间/devel/lib/record_bag/record_bag)放入RecordBag/usr/lib/record_bag/目录下 在RecordBag/usr/lib/record_bag/目录下创建copy.sh脚本并赋予权限,内容如下: #!/bin/sh exe="record_bag" #可执行文件名 des="/home/miao/RecordBag/usr/lib" #创建文件夹的下的/usr/lib目录 deplist=$(ldd$exe|awk'{if(match($3,"/")){printf("%s"),$3}}') cp$deplist$des 复制 脚本内容自行修改,若直接复制清删除中文注释 在RecordBag/usr/lib

  • canal简介

    https://www.cnblogs.com/javazhiyin/p/11676043.html https://www.cnblogs.com/LQBlog/p/12177295.html#autoid-2-0-0

  • urllib2

    【urllib2】 1、基本用法。    2、geturl()方法    3、urllib.urlencode方法。    4、添加post数据。    5、连接的过程最容易出错。     

  • JS控制消除文本框中的空格符号

    <html> <head> <title>JS控制消除文本框中的空格符号丨kiddy</title> <SCRIPTLANGUAGE="JavaScript"> <!--Begin functionignoreSpaces(string){ vartemp=""; string=''+string; splitstring=string.split(""); for(i=0;i<splitstring.length;i++) temp+=splitstring[i]; returntemp; } //End--> </script> </head> <body> <center> <pre> </pre><fontsize="2">随意录入几个中间带空格的字符试试</font>. </center> <center> <form> <inputtype=textsize=2

  • 进入Docker容器报错docker exec -it wsqtest bash

    一、复现报错: dockerexec-it wsqtest bash rpcerror:code=2desc=ociruntimeerror:execfailed: container_linux.go:247:startingcontainerprocesscaused"process_linux.go:110: decodinginiterrorfrompipecaused\"readparent:connectionresetbypeer\""复制 二、解决方案: itseemstobeabugintroducedinarecentupdatetothedockerRPMpackages.Atemporaryworkaroundistodowngradealldockerpackagestoapreviousrelease(75seemstowork). yumdowngradedocker-1.13.1-75.git8633870.el7_5\ docker-client-1.13.1-75.git8633870.el7_5\ docker-common-1

  • NOIP-Vigen&#232;re密码

      题目描述 16世纪法国外交家BlaisedeVigenère设计了一种多表密码加密算法――Vigenère密码。Vigenère密码的加密解密算法简单易用,且破译难度比较高,曾在美国南北战争中为南军所广泛使用。 在密码学中,我们称需要加密的信息为明文,用M表示;称加密后的信息为密文,用C表示;而密钥是一种参数,是将明文转换为密文或将密文转换为明文的算法中输入的数据,记为k。在Vigenère密码中,密钥k是一个字母串,k=k1,k2,…,kn。当明文M=m1,m2,…,mn时,得到的密文C=c1,c2,…,cn,其中ci=mi®ki,运算®的规则如下表所示: Vigenère加密在操作时需要注意:1.®运算忽略参与运算的字母的大小写,并保持字母在明文M中的大小写形式;2.当明文M的长度大于密钥k的长度时,将密钥k重复使用。例如,明文M=Helloworld,密钥k=abc时,密文C=Hfnlpyosnd。 输入描述: 共2行。第一行为一个字符串,表示密钥k,长度不超过100,其中仅包含大小写字母。第二行为一个字符串,表示经加密后的密文,长度不超过1000,

相关推荐

推荐阅读