我正在尝试将经过训练的模型移至生产环境中,并且遇到了一个问题,试图在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)
我尝试过的事情:
下面的代码是我对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作为解决方案类型。
答案 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的哈希函数实现在将数据集输入模型之前对其进行预哈希处理。
此解决方案易于实现,并且可以像原始文本哈希一样工作。对于那些像我这样尝试跨语言哈希的人来说,建议是:不要这样做,这很令人头疼!使用一种语言对文本数据进行哈希处理,并找到一种使用所有必需信息来创建有效数据集的方法。