繁体   English   中英

如何训练 LSTM 对 tensorflow.js 中的垃圾邮件进行分类?

[英]How can you train an LSTM to classify spam in tensorflow.js?

我正在针对一些垃圾邮件训练 LSTM - 我有两个类:“垃圾邮件”和“火腿”。 我通过将每条消息拆分为字符然后对字符进行单热编码来预处理数据。 然后我将它归因于相应的向量 - [0] 代表“火腿”,[1] 代表“垃圾邮件”。 此代码预处理数据:

const fs = require("fs");
const R = require("ramda");
const txt = fs.readFileSync("spam.txt").toString();
const encodeChars = string => {
    const vecLength = 127;
    const genVec = (char) => R.update(char.charCodeAt(0), 1, Array(vecLength).fill(0));
    return string.split('').map(char => char.charCodeAt(0) < vecLength ? genVec(char) : "invalid");
}
const data = R.pipe(
    R.split(",,,"),
    R.map(
        R.pipe(
            x => [(x.split(",").slice(1).concat("")).reduce((t, v) => t.concat(v)), x.split(",")[0]],
            R.adjust(1, R.replace(/\r|\n/g, "")),
            R.adjust(0, encodeChars),
            R.adjust(1, x => x === "ham" ? [0] : [1])
        )
    ),
    R.filter(R.pipe(
        R.prop(0),
        x => !R.contains("invalid", x)
    ))
)(txt);
fs.writeFileSync("data.json", JSON.stringify(data))

然后,使用来自data.json的编码向量,我将数据移植到 tensorflow:

const fs = require("fs");
const data = JSON.parse(fs.readFileSync("data.json").toString()).sort(() => Math.random() - 0.5)
const train = data.slice(0, Math.floor(data.length * 0.8));
const test = data.slice(Math.floor(data.length * 0.8));
const tf = require("@tensorflow/tfjs-node");
const model = tf.sequential({
    layers: [
        tf.layers.lstm({ inputShape: [null, 127], units: 16, activation: "relu", returnSequences: true }),
        tf.layers.lstm({ units: 16, activation: "relu", returnSequences: true }),
        tf.layers.lstm({ units: 16, activation: "relu", returnSequences: true }),
        tf.layers.dense({ units: 1, activation: "softmax" }),
    ]
})
const tdata = tf.tensor3d(train.map(x => x[0]));
const tlabels = tf.tensor2d(train.map(x => x[1]));
model.compile({
    optimizer: "adam",
    loss: "categoricalCrossentropy",
    metrics: ["accuracy"]
})
model.fit(tdata, tlabels, {
    epochs: 1,
    batchSize: 32,
    callbacks: {
        onBatchEnd(batch, logs) {
            console.log(logs.acc)
        }
    }
})

tdata 是 3 维的,tlabels 是 2 维的,所以一切正常。 但是,当我运行代码时,出现以下错误: Error when checking target: expected dense_Dense1 to have 3 dimension(s). but got array with shape 4032,1 Error when checking target: expected dense_Dense1 to have 3 dimension(s). but got array with shape 4032,1有谁知道这里出了什么问题 - 我想不通。 谢谢!

注意:我已经尝试通过在消息向量的末尾添加“null”以将它们全部置于标准化长度来规范化向量的长度。 我仍然遇到同样的错误。

LSTM 的最后一层应该设置returnSequences: false ,相当于一个 flatten 层。 这将修复答案的错误。

检查目标时出错:预期dense_Dense1 有3 个维度。 但得到了形状为 4032,1 的数组

为了详细说明答案,不仅仅是字符编码。 实际上,不是对每个字符进行编码,而是应该对数据集进行标记化。 可以按照此处的说明使用简单的单词分词器,也可以使用通用句子编码器附带的分词器 LSTM 序列可以由每个令牌的唯一标识符组成。

此外,对最后一层使用单个单元并不反映分类方法。 这更像是我们在预测一个值而不是一个类。 应该使用两个单元(一个用于垃圾邮件,另一个用于火腿)以便对​​标签进行 onehot 编码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM