我想使用来自Keras机器学习模型的权重和偏差,在没有安装Keras(并且无法安装)的另一个程序中创建一个数学预测函数。
我有一个简单的多层感知机模型,用于拟合数据。我在Python中运行,使用的后端是Keras和TensorFlow;目前,我使用了一个输入层、一个隐藏层和一个输出层。所有层都使用RELU激活函数,我的优化器是adam,损失函数是均方误差。
据我所知,层的权重应该以数学形式使用,如下所示:
(SUM (w*i)) + b
其中求和是针对所有权重和输入的,b是神经元的偏差。例如,假设我的输入层形状为(33, 64)。有33个输入和64个神经元。我将有一个维度为33的向量输入和一个维度为64的向量输出。这将使每个SUM包含33个项*33个权重,输出将是所有64个SUM加上相应的64个偏差。
下一层,在我的例子中是32个神经元,将执行相同的操作,但有64个输入和32个输出。我的输出层将输出一个单一值,因此输入32,输出1。
我已经编写了代码来尝试模拟这个模型。以下是进行单一预测的一个代码片段:
def modelR(weights, biases, data): # 这是输入层。 y = [] for i in range(len(weights[0][0])): x = np.zeros(len(weights[0][0])) for j in range(len(data)): x[i] += weights[0][j][i]*data[j] y.append(x[i]+biases[0][i]) # 这是隐藏层。 z = [] for i in range(len(weights[1][0])): x = np.zeros(len(weights[1][0])) for j in range(len(y)): x[i] += weights[1][j][i]*y[j] z.append(x[i]+biases[1][i]) # 这是输出层。 p = 0.0 for i in range(len(z)): p += weights[-1][i][0]*z[i] p = p+biases[-1][0] return p
需要说明的是,“weights”和“biases”是通过以下方式获得的:
weights = [] biases = [] for i in range(len(model.layers)): weights.append(model.layers[i].get_weights()[0]) biases.append(model.layers[i].get_weights()[1]) weights = np.asarray(weights) biases = np.asarray(biases)
因此,第一个输入的第一个神经元的第一个权重是weight[0][0][0],第二个神经元的第一个输入的第一个权重是weight[0][1][0],依此类推。我可能在这方面理解错了,这可能是我卡住的地方。但这似乎合理,因为我们是从(1 x 33)的向量到(1 x 64)的向量,所以我们应该有一个(33 x 64)的矩阵。
有哪里我做错了的想法吗?谢谢!
编辑:答案找到了我将jhso的答案标记为正确,尽管它在我的代码中没有正常工作(我可能遗漏了某个导入语句)。关键在于激活函数。我使用的是RELU,所以我不应该传递任何负值。此外,jhso展示了一种不使用循环而直接进行矩阵乘法的好方法(我不知道Python可以这样做)。现在我只需要弄清楚如何在c++中实现它!
回答:
我认为在处理机器学习时熟悉线性代数是很好的。当我们有一个形式为sum(matrix elem times another matrix elem)
的方程时,通常是简单地进行矩阵乘法,形式为matrix1 * matrix2.T
。这大大简化了你的代码:
def modelR(weights, biases, data): # 这是输入层。 y = np.matmul(data,weights[0])+biases[0][None,:] y_act = relu(y) #在这里也可以使用dropout或任何其他函数 z = np.matmul(y_act,weights[1])+biases[1][None,:] z_act = relu(z) #在这里也可以使用dropout和任何其他函数 p = np.matmul(z_act,weights[2])+biases[2][None,:] p_act = sigmoid(p) return p_act
我对你使用的激活函数做了一个猜测。我也不确定你的数据是如何组织的,只需确保特征/权重始终是乘法的内维度,即,如果你的输入是(Bx10)而你的权重是(10×64),那么input*weights
就足够了,并且将产生形状为(Bx64)的输出。