文字杂凑技巧在Python和C#中产生不同的结果

时间:2018-07-24 15:32:29

标签: c# python nlp

我正在尝试将经过训练的模型移至生产环境中,并且遇到了一个问题,试图在C#中复制Keras hashing_trick()函数的行为。当我对句子进行编码时,C#中的输出与python中的输出不同:

文本:“信息-配置处理已完成。”

Python:[0 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 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();
        }
    }

可以找到here

的哈希技巧的keras实现

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

2 个答案:

答案 0 :(得分:1)

您的代码最大的问题是它无法说明Python的int是一个任意精度的整数,而C#的uint只有32位。这意味着Python正在计算哈希的所有128位的模,而C#则不是(并且BitConverter.ToUInt32在任何情况下都是错误的做法,因为字节序是错误的)。使您烦恼的另一个问题是\0不会在C#中终止字符串,并且\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

此代码效率低下:与使用StringBuilder相比,使用LINQ过滤字符效率非常低,并且我们这里实际上并不需要BigInteger,因为MD5始终是精确的128位,但是进行了优化(如果留给读者作为练习,留给结果填充(您已经有此功能)。

答案 1 :(得分:0)

我没有解决尝试与C#战斗以获得正确的哈希值的问题,而是采取了另一种方法来解决此问题。在建立数据集以训练模型时(毕竟这是一个机器学习项目),我决定使用@Jeron Mostert的哈希函数实现在将数据集输入模型之前对其进行预哈希处理。

此解决方案易于实现,并且可以像原始文本哈希一样工作。对于那些像我这样尝试跨语言哈希的人来说,建议是:不要这样做,这很令人头疼!使用一种语言对文本数据进行哈希处理,并找到一种使用所有必需信息来创建有效数据集的方法。