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



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

文本:"信息-配置处理完成。">

Python:[0 0 0 0 0[0 0 0 0 0 217 142 262 113 319 413]

C#:[0,0,0,0,0,0,00,0:0,0:00,0[0,0,433,426,425,461,336,146,52]

(从调试器复制,两个序列的长度都为30(

我尝试过的:

  1. 更改C#中文本字节的编码以匹配python字符串.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 = "!#$%&()*+,-./:;<=>?@[\]^_`{|}~tn".ToCharArray().ToList();
filters.ForEach(x =>
{
data = data.Replace(x, '');
});
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作为我的解决方案类型。

您的代码最大的问题是它没有考虑到Python的int是一个任意精度的整数,而C#的uint只有32位。这意味着Python正在计算哈希的所有128位的模,而C#则不是(而且BitConverter.ToUInt32在任何情况下都是错误的,因为endianness是错误的(。另一个让您感到困惑的问题是,不会终止C#中的字符串,并且不能在不更改结果的情况下直接添加到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: "!#$%&()*+,-./:;<=>?@[\]^_`{|}~tn",
lower: true,
split: " "
).Select(i => i.ToString())
));

217 142 262 113 319 413

这段代码有各种低效之处:与使用StringBuilder相比,使用LINQ过滤字符的效率非常低,而且我们在这里并不真正需要BigInteger,因为MD5总是正好是128位,但优化(如果必要(是留给读者的练习,填充结果也是如此(您已经有了一个函数(。

我没有解决试图与C#对抗以获得正确哈希的问题,而是采取了不同的方法来解决这个问题。当我制作数据集来训练模型时(毕竟这是一个机器学习项目(,我决定使用@Jeron Mostert的哈希函数实现,在将数据集输入模型之前对其进行预哈希。

这个解决方案更容易实现,并且最终与原始文本哈希一样有效。建议那些像我一样尝试跨语言哈希的人:不要这样做,这很头疼!使用一种语言对文本数据进行哈希处理,并找到一种方法来创建一个包含所有所需信息的有效数据集。

最新更新