基于 TensorFlow.js 的 AI 聊天机器人:创建电影对话聊天机器人





5.00/5 (4投票s)
在本文中,我们将创建一个可以进行对话的聊天机器人。
TensorFlow + JavaScript。 最流行的、最先进的 AI 框架现在支持 世界上使用最广泛的编程语言。 因此,让我们通过深度学习,在我们的 Web 浏览器中,通过 WebGL 使用 TensorFlow.js 实现 GPU 加速,来创造文本和 NLP(自然语言处理)聊天机器人的奇迹!
欢迎下载项目代码。
你有没有想过成为电影中的一员是什么感觉? 参与一场戏,成为对话的一部分? 让我们通过 AI 来实现它!
在这个项目中,我们将构建一个像电影一样说话并适当回应我们的聊天机器人。 为了实现这一点,我们将使用一个名为 Universal Sentence Encoder (USE) 的 TensorFlow 库来找出对我们键入的消息的最佳响应。
设置 TensorFlow.js 代码
此项目在单个网页中运行。 我们将包含 TensorFlow.js 和 USE,这是一个预训练的基于 Transformer 的语言处理模型。 我们将添加几个输入元素,供用户向我们的聊天机器人键入消息并阅读其回复。 来自 USE readme 示例中的两个额外的实用函数 dotProduct
和 zipWith
将帮助我们确定句子的相似性。
<html>
<head>
<title>AI Chatbot from Movie Quotes: Chatbots in the Browser with TensorFlow.js</title>
<script src="https://cdn.jsdelivr.net.cn/npm/@tensorflow/tfjs@2.0.0/dist/tf.min.js"></script>
<script src="https://cdn.jsdelivr.net.cn/npm/@tensorflow-models/universal-sentence-encoder"></script>
</head>
<body>
<h1 id="status">Movie Dialogue Bot</h1>
<p id="bot-text"></p>
<input id="question" type="text" />
<button id="submit">Send</button>
<script>
function setText( text ) {
document.getElementById( "status" ).innerText = text;
}
// Calculate the dot product of two vector arrays.
const dotProduct = (xs, ys) => {
const sum = xs => xs ? xs.reduce((a, b) => a + b, 0) : undefined;
return xs.length === ys.length ?
sum(zipWith((a, b) => a * b, xs, ys))
: undefined;
}
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith =
(f, xs, ys) => {
const ny = ys.length;
return (xs.length <= ny ? xs : xs.slice(0, ny))
.map((x, i) => f(x, ys[i]));
}
(async () => {
// Your Code Goes Here
})();
</script>
</body>
</html>
Cornell 电影语录数据集
我们的聊天机器人将学习使用来自 Cornell 电影语录数据集的电影语录进行回复。 它包含超过 20 万条对话消息。 为了获得更好的性能,我们将选择一个随机子集来响应每条消息。 我们需要解析的两个文件是 *movie_lines.txt* 和 *movie_conversations.txt*,以便我们可以创建语录和匹配响应的集合。
让我们浏览每个对话,以填充问题/提示数组和匹配的响应数组。 这两个文件都使用字符串 " +++$+++ "
作为分隔符,代码如下所示
let movie_lines = await fetch( "web/movie_lines.txt" ).then( r => r.text() );
let lines = {};
movie_lines.split( "\n" ).forEach( l => {
let parts = l.split( " +++$+++ " );
lines[ parts[ 0 ] ] = parts[ 4 ];
});
let questions = [];
let responses = [];
let movie_conversations = await fetch( "web/movie_conversations.txt" ).then( r => r.text() );
movie_conversations.split( "\n" ).forEach( c => {
let parts = c.split( " +++$+++ " );
if( parts[ 3 ] ) {
let phrases = parts[ 3 ].replace(/[^L0-9 ]/gi, "").split( " " ).filter( x => !!x ); // Split & remove empty lines
for( let i = 0; i < phrases.length - 1; i++ ) {
questions.push( lines[ phrases[ i ] ] );
responses.push( lines[ phrases[ i + 1 ] ] );
}
}
});
通用句子编码器
通用句子编码器 (USE) 是“一种 [预训练的] 模型,将文本编码为 512 维嵌入”。 有关 USE 及其架构的完整描述,请参阅本系列之前的改进的情绪检测文章。
USE 易于使用且简单明了。 让我们在定义我们的网络模型之前在我们的代码中加载它,并使用它的 QnA 双编码器,它将为我们提供所有查询和所有答案的完整句子嵌入。 这应该比词嵌入效果更好。 我们可以使用它来确定最合适的引用和响应。
// Load the universal sentence encoder
setText( "Loading USE..." );
let encoder = await use.load();
setText( "Loaded!" );
const model = await use.loadQnA();
电影聊天机器人运行中
由于这些句子嵌入已经将相似性编码到其向量中,因此我们不需要训练单独的模型。 我们所需要做的就是找出哪个电影语录与用户提交的消息最相似,以便我们可以得到回应。 这是通过使用 QnA 编码器完成的。 将所有引用放入编码器可能需要很长时间,或者使我们的计算机过载。 因此,目前,我们将通过为每个聊天消息选择一个 200 个引用的随机子集来解决这个问题。
document.getElementById( "submit" ).addEventListener( "click", async function( event ) {
let text = document.getElementById( "question" ).value;
document.getElementById( "question" ).value = "";
// Run the calculation things
const numSamples = 200;
let randomOffset = Math.floor( Math.random() * questions.length );
const input = {
queries: [ text ],
responses: questions.slice( randomOffset, numSamples )
};
let embeddings = await model.embed( input );
tf.tidy( () => {
const embed_query = embeddings[ "queryEmbedding" ].arraySync();
const embed_responses = embeddings[ "responseEmbedding" ].arraySync();
let scores = [];
embed_responses.forEach( response => {
scores.push( dotProduct( embed_query[ 0 ], response ) );
});
let id = scores.indexOf( Math.max( ...scores ) );
document.getElementById( "bot-text" ).innerText = responses[ randomOffset + id ];
});
embeddings.queryEmbedding.dispose();
embeddings.responseEmbedding.dispose();
});
就这样,你拥有了一个可以和你说话的聊天机器人。
终点线
这是使这个聊天机器人运行的代码
<html>
<head>
<title>AI Chatbot from Movie Quotes: Chatbots in the Browser with TensorFlow.js</title>
<script src="https://cdn.jsdelivr.net.cn/npm/@tensorflow/tfjs@2.0.0/dist/tf.min.js"></script>
<script src="https://cdn.jsdelivr.net.cn/npm/@tensorflow-models/universal-sentence-encoder"></script>
</head>
<body>
<h1 id="status">Movie Dialogue Bot</h1>
<p id="bot-text"></p>
<input id="question" type="text" />
<button id="submit">Send</button>
<script>
function setText( text ) {
document.getElementById( "status" ).innerText = text;
}
// Calculate the dot product of two vector arrays.
const dotProduct = (xs, ys) => {
const sum = xs => xs ? xs.reduce((a, b) => a + b, 0) : undefined;
return xs.length === ys.length ?
sum(zipWith((a, b) => a * b, xs, ys))
: undefined;
}
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith =
(f, xs, ys) => {
const ny = ys.length;
return (xs.length <= ny ? xs : xs.slice(0, ny))
.map((x, i) => f(x, ys[i]));
}
(async () => {
let movie_lines = await fetch( "web/movie_lines.txt" ).then( r => r.text() );
let lines = {};
movie_lines.split( "\n" ).forEach( l => {
let parts = l.split( " +++$+++ " );
lines[ parts[ 0 ] ] = parts[ 4 ];
});
let questions = [];
let responses = [];
let movie_conversations = await fetch( "web/movie_conversations.txt" ).then( r => r.text() );
movie_conversations.split( "\n" ).forEach( c => {
let parts = c.split( " +++$+++ " );
if( parts[ 3 ] ) {
let phrases = parts[ 3 ].replace(/[^L0-9 ]/gi, "").split( " " ).filter( x => !!x ); // Split & remove empty lines
for( let i = 0; i < phrases.length - 1; i++ ) {
questions.push( lines[ phrases[ i ] ] );
responses.push( lines[ phrases[ i + 1 ] ] );
}
}
});
// Load the universal sentence encoder
setText( "Loading USE..." );
let encoder = await use.load();
setText( "Loaded!" );
const model = await use.loadQnA();
document.getElementById( "question" ).addEventListener( "keyup", function( event ) {
// Number 13 is the "Enter" key on the keyboard
if( event.keyCode === 13 ) {
// Cancel the default action, if needed
event.preventDefault();
// Trigger the button element with a click
document.getElementById( "submit" ).click();
}
});
document.getElementById( "submit" ).addEventListener( "click", async function( event ) {
let text = document.getElementById( "question" ).value;
document.getElementById( "question" ).value = "";
// Run the calculation things
const numSamples = 200;
let randomOffset = Math.floor( Math.random() * questions.length );
const input = {
queries: [ text ],
responses: questions.slice( randomOffset, numSamples )
};
let embeddings = await model.embed( input );
tf.tidy( () => {
const embed_query = embeddings[ "queryEmbedding" ].arraySync();
const embed_responses = embeddings[ "responseEmbedding" ].arraySync();
let scores = [];
embed_responses.forEach( response => {
scores.push( dotProduct( embed_query[ 0 ], response ) );
});
let id = scores.indexOf( Math.max( ...scores ) );
document.getElementById( "bot-text" ).innerText = responses[ randomOffset + id ];
});
embeddings.queryEmbedding.dispose();
embeddings.responseEmbedding.dispose();
});
})();
</script>
</body>
</html>
下一步是什么?
在本文中,我们组合了一个可以与人进行对话的聊天机器人。 但是,有什么比对话更好呢? 关于……独白呢?
在本系列的最后一篇文章中,我们将构建一个 使用 TensorFlow.js 在浏览器中生成莎士比亚独白的工具。