Python和C#中的文本哈希技巧产生不同的结果

3
我正在尝试将一个已训练的模型移植到生产环境中,并在尝试在C#中复制Keras hashing_trick()函数的行为时遇到了问题。当我尝试对句子进行编码时,C#中的输出与Python中的输出不同:
文本:"Information - The configuration processing is completed."
Python:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 217 142 262 113 319 413]
C#:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 433, 426, 425, 461, 336, 146, 52]
(从调试器中复制,两个序列长度均为30)
我的尝试:
1. 将C#中文本字节的编码更改为与Python string.encode()函数默认值(UTF8)相匹配
2. 更改字母的大小写
3. 尝试使用Convert.ToUInt32代替BitConverter(结果导致溢出错误)
我的代码(如下)是我实现Keras hashing_trick函数的方式。给定一个输入句子,然后该函数将返回相应的编码序列。
public uint[] HashingTrick(string data)
    {
        const int VOCAB_SIZE = 534; //Determined through python debugging of model
        var filters = "!#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n".ToCharArray().ToList();
        filters.ForEach(x =>
        {
            data = data.Replace(x, '\0');
        });
        string[] parts = data.Split(' ');
        var encoded = new List<uint>();
        parts.ToList().ForEach(x =>
        {

            using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
            {
                byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(x);
                byte[] hashBytes = md5.ComputeHash(inputBytes);


                uint val = BitConverter.ToUInt32(hashBytes, 0);
                encoded.Add(val % (VOCAB_SIZE - 1) + 1);
            }
        });
        return PadSequence(encoded, 30);

    }
    private uint[] PadSequence(List<uint> seq, int maxLen)
    {
        if (seq.Count < maxLen)
        {
            while (seq.Count < maxLen)
            {
                seq.Insert(0, 0);
            }
            return seq.ToArray();
        }
        else if (seq.Count > maxLen)
        {
            return seq.GetRange(seq.Count - maxLen - 1, maxLen).ToArray();
        }
        else
        {
            return seq.ToArray();
        }
    }

您可以在此处找到keras实现的哈希技巧:这里

如果有帮助的话,我正在使用ASP.NET Web API作为我的解决方案类型。

2个回答

1
你的代码最大的问题在于没有考虑到Python的int是任意精度整数,而C#的uint只有32位。这意味着Python在所有128位的哈希上计算模数,而C#则不会(而且BitConverter.ToUInt32无论如何都是错误的事情,因为字节顺序是错误的)。另一个让你困惑的问题是,在C#中,\0不能终止字符串,而将\0添加到MD5哈希中会改变结果。
int[] hashingTrick(string text, int n, string filters, bool lower, string split) {
    var splitWords = String.Join("", text.Where(c => !filters.Contains(c)))
        .Split(new[] { split }, StringSplitOptions.RemoveEmptyEntries);

    return (
        from word in splitWords
        let bytes = Encoding.UTF8.GetBytes(lower ? word.ToLower() : word)
        let hash = MD5.Create().ComputeHash(bytes)
        // add a 0 byte to force a non-negative result, per the BigInteger docs 
        let w = new BigInteger(hash.Reverse().Concat(new byte[] { 0 }).ToArray())
        select (int) (w % (n - 1) + 1)
    ).ToArray();
}

示例用法:

const int vocabSize = 534;
Console.WriteLine(String.Join(" ",
    hashingTrick(
        text: "Information - The configuration processing is completed.",
        n: vocabSize,
        filters: "!#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n",
        lower: true,
        split: " "
    ).Select(i => i.ToString())
));

217 142 262 113 319 413

这段代码存在多种低效:使用LINQ过滤字符与使用StringBuilder相比非常低效,而且我们这里并不需要BigInteger,因为MD5始终是恰好128位,但优化(如果必要)留给读者自己思考,填充结果的工作也是一样(您已经有一个函数可以完成此任务)。

谢谢您提供的示例!看起来这段代码适用于较短的句子(比如问题中的那个),但对于更长、更详细的句子(假设这些句子可能超过100个单词),输出结果不正确。这是因为我有一个更大的字符串,因此需要跨越更多的位进行哈希处理吗? - Michael George
我不知道那个神秘的数字534从哪里来,这将是我的首要调查线索。其次,要看哪些数字是正确的 - 每个整数对应上述代码中的一个散列单词,因此如果存在差异,则是由于字符串被分割方式不同或结果被收集方式不同所致。我没有很仔细地调试Python代码。(例如,尝试“configuration configuration configuration”- Python会生成三个数字还是只有一个?) - Jeroen Mostert
顺带一提,根据您的应用场景,您可能想要看看类似于IronPython这样的东西,直接运行您的Python代码,而不是先将代码翻译成C#(您可以使用C#来提供API端点,并在同一个应用程序中调用Python代码)。重新发明轮子从来都不是一个好主意,尤其是当现有的轮子完全正常时。 - Jeroen Mostert
神秘的数字534来自我正在处理的数据集中总不同单词的数量。这个数字用于创建哈希以及在Keras模型内的嵌入层中使用。我尝试使用IronPython,但是Keras使用的hashlib库不受IronPython支持。这是因为hashlib是C扩展,而IronPython不支持C扩展。我正在调查一个名为ironclad的项目,旨在解决此问题。我会报告结果。 - Michael George
@MichaelGeorge:IronPython代码应该能够调用任何.NET类,因此您实际上可以用 .NET 自己的 MD5 类替换 hashlib,这只需要进行最小化的代码调整(假设在程序中一直使用的是 MD5)。 - Jeroen Mostert
我的初始印象是你的想法只是将问题移至其他位置。然而,在你的回答中,你指出了Python与C#不同地处理整数类的观点是有道理的。我不确定是否值得使用.NET MD5函数,特别是因为执行嵌入式Python代码似乎会显著减缓系统的运行速度。 - Michael George

0

与其试图解决使用C#进行哈希的问题,我采用了一种不同的方法来解决这个问题。在创建我的数据集以训练模型时(毕竟这是一个机器学习项目),我决定使用@Jeron Mostert的哈希函数实现对数据集进行预哈希处理,然后再将其馈送到模型中。

这种方法更容易实现,并最终与原始文本哈希一样有效。对于那些像我一样尝试进行跨语言哈希的人,建议别这么做,这会给你带来很多麻烦!使用一种语言对文本数据进行哈希,并找到一种方式来创建一个包含所有所需信息的有效数据集。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接