- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我正在尝试用 Java 实现多元梯度下降算法(来自 AI coursera 类(class)),但我无法确定我的代码中的错误位置。
这是下面程序的输出:
Before train: parameters := [0.0, 0.0, 0.0] -> cost function := 2.5021875E9
After first iteration: parameters := [378.5833333333333, 2.214166666666667, 50043.75000000001] -> cost function := 5.404438291015627E9
如您所见,在第一次迭代后,这些值相差甚远。我做错了什么?
这是我要实现的算法:
还有代码:
import java.util.*;
public class GradientDescent {
private double[][] trainingData;
private double[] means;
private double[] scale;
private double[] parameters;
private double learningRate;
GradientDescent() {
this.learningRate = 0D;
}
public double predict(double[] inp){
double[] features = new double[inp.length + 1];
features[0] = 1;
for(int i = 0; i < inp.length; i++) {
features[i+1] = inp[i];
}
double prediction = 0;
for(int i = 0; i < parameters.length; i++) {
prediction = parameters[i] * features[i];
}
return prediction;
}
public void train(){
double[] tempParameters = new double[parameters.length];
for(int i = 0; i < parameters.length; i++) {
tempParameters[i] = parameters[i] - learningRate * partialDerivative(i);
//System.out.println(tempParameters[i] + " = " + parameters[i] + " - " + learningRate + " * " + partialDerivative(i));
}
System.out.println("Before train: parameters := " + Arrays.toString(parameters) + " -> cost function := " + costFunction());
parameters = tempParameters;
System.out.println("After first iteration: parameters := " + Arrays.toString(parameters) + " -> cost function := " + costFunction());
}
private double partialDerivative(int index) {
double sum = 0;
for(int i = 0; i < trainingData.length; i++) {
double[] input = new double[trainingData[i].length - 1];
int j = 0;
for(; j < trainingData[i].length - 1; j++) {
input[j] = trainingData[i][j];
}
sum += ((predict(input) - trainingData[i][j]) * trainingData[i][index]);
}
return (1D/trainingData.length) * sum;
}
public double[][] getTrainingData() {
return trainingData;
}
public void setTrainingData(double[][] data) {
this.trainingData = data;
this.means = new double[this.trainingData[0].length-1];
this.scale = new double[this.trainingData[0].length-1];
for(int j = 0; j < data[0].length-1; j++) {
double min = data[0][j], max = data[0][j];
double sum = 0;
for(int i = 0; i < data.length; i++) {
if(data[i][j] < min) min = data[i][j];
if(data[i][j] > max) max = data[i][j];
sum += data[i][j];
}
scale[j] = max - min;
means[j] = sum / data.length;
}
}
public double[] getParameters() {
return parameters;
}
public void setParameters(double[] parameters) {
this.parameters = parameters;
}
public double getLearningRate() {
return learningRate;
}
public void setLearningRate(double learningRate) {
this.learningRate = learningRate;
}
/** 1 m i i 2
* J(theta) = ----- * SUM( h (x ) - y )
* 2*m i=1 theta
*/
public double costFunction() {
double sum = 0;
for(int i = 0; i < trainingData.length; i++) {
double[] input = new double[trainingData[i].length - 1];
int j = 0;
for(; j < trainingData[i].length - 1; j++) {
input[j] = trainingData[i][j];
}
sum += Math.pow(predict(input) - trainingData[i][j], 2);
}
double factor = 1D/(2*trainingData.length);
return factor * sum;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("hypothesis: ");
int i = 0;
sb.append(parameters[i++] + " + ");
for(; i < parameters.length-1; i++) {
sb.append(parameters[i] + "*x" + i + " + ");
}
sb.append(parameters[i] + "*x" + i);
sb.append("\n Feature scale: ");
for(i = 0; i < scale.length-1; i++) {
sb.append(scale[i] + " ");
}
sb.append(scale[i]);
sb.append("\n Feature means: ");
for(i = 0; i < means.length-1; i++) {
sb.append(means[i] + " ");
}
sb.append(means[i]);
sb.append("\n Cost fuction: " + costFunction());
return sb.toString();
}
public static void main(String[] args) {
final double[][] TDATA = {
{200, 2, 20000},
{300, 2, 41000},
{400, 3, 51000},
{500, 3, 61500},
{800, 4, 41000},
{900, 5, 141000}
};
GradientDescent gd = new GradientDescent();
gd.setTrainingData(TDATA);
gd.setParameters(new double[]{0D,0D,0D});
gd.setLearningRate(0.00001);
gd.train();
//System.out.println(gd);
//System.out.println("PREDICTION: " + gd.predict(new double[]{300, 2}));
}
}
编辑:
我更新了代码以使其更具可读性,并尝试将其映射到 Douglas 使用的符号。我认为它现在工作得更好了,但仍有一些我不完全理解的阴暗区域。
似乎如果我有多个参数(如下例所示,房间数和面积),则预测与第二个参数(在本例中为面积)密切相关,并且改变影响不大第一个参数(房间数)。
这是对 {2, 200}
的预测:
PREDICTION: 200000.00686158828
这是对 {5, 200}
的预测:
PREDICTION: 200003.0068315415
如您所见,这两个值之间几乎没有任何区别。
我尝试将数学转化为代码的尝试是否仍然存在错误?
这是更新后的代码:
import java.util.*;
public class GradientDescent {
private double[][] trainingData;
private double[] means;
private double[] scale;
private double[] parameters;
private double learningRate;
GradientDescent() {
this.learningRate = 0D;
}
public double predict(double[] inp) {
return predict(inp, this.parameters);
}
private double predict(double[] inp, double[] parameters){
double[] features = concatenate(new double[]{1}, inp);
double prediction = 0;
for(int j = 0; j < features.length; j++) {
prediction += parameters[j] * features[j];
}
return prediction;
}
public void train(){
readjustLearningRate();
double costFunctionDelta = Math.abs(costFunction() - costFunction(iterateGradient()));
while(costFunctionDelta > 0.0000000001) {
System.out.println("Old cost function : " + costFunction());
System.out.println("New cost function : " + costFunction(iterateGradient()));
System.out.println("Delta: " + costFunctionDelta);
parameters = iterateGradient();
costFunctionDelta = Math.abs(costFunction() - costFunction(iterateGradient()));
readjustLearningRate();
}
}
private double[] iterateGradient() {
double[] nextParameters = new double[parameters.length];
// Calculate parameters for the next iteration
for(int r = 0; r < parameters.length; r++) {
nextParameters[r] = parameters[r] - learningRate * partialDerivative(r);
}
return nextParameters;
}
private double partialDerivative(int index) {
double sum = 0;
for(int i = 0; i < trainingData.length; i++) {
int indexOfResult = trainingData[i].length - 1;
double[] input = Arrays.copyOfRange(trainingData[i], 0, indexOfResult);
sum += ((predict(input) - trainingData[i][indexOfResult]) * trainingData[i][index]);
}
return sum/trainingData.length ;
}
private void readjustLearningRate() {
while(costFunction(iterateGradient()) > costFunction()) {
// If the cost function of the new parameters is higher that the current cost function
// it means the gradient is diverging and we have to adjust the learning rate
// and recalculate new parameters
System.out.print("Learning rate: " + learningRate + " is too big, readjusted to: ");
learningRate = learningRate/2;
System.out.println(learningRate);
}
// otherwise we are taking small enough steps, we have the right learning rate
}
public double[][] getTrainingData() {
return trainingData;
}
public void setTrainingData(double[][] data) {
this.trainingData = data;
this.means = new double[this.trainingData[0].length-1];
this.scale = new double[this.trainingData[0].length-1];
for(int j = 0; j < data[0].length-1; j++) {
double min = data[0][j], max = data[0][j];
double sum = 0;
for(int i = 0; i < data.length; i++) {
if(data[i][j] < min) min = data[i][j];
if(data[i][j] > max) max = data[i][j];
sum += data[i][j];
}
scale[j] = max - min;
means[j] = sum / data.length;
}
}
public double[] getParameters() {
return parameters;
}
public void setParameters(double[] parameters) {
this.parameters = parameters;
}
public double getLearningRate() {
return learningRate;
}
public void setLearningRate(double learningRate) {
this.learningRate = learningRate;
}
/** 1 m i i 2
* J(theta) = ----- * SUM( h (x ) - y )
* 2*m i=1 theta
*/
public double costFunction() {
return costFunction(this.parameters);
}
private double costFunction(double[] parameters) {
int m = trainingData.length;
double sum = 0;
for(int i = 0; i < m; i++) {
int indexOfResult = trainingData[i].length - 1;
double[] input = Arrays.copyOfRange(trainingData[i], 0, indexOfResult);
sum += Math.pow(predict(input, parameters) - trainingData[i][indexOfResult], 2);
}
double factor = 1D/(2*m);
return factor * sum;
}
private double[] normalize(double[] input) {
double[] normalized = new double[input.length];
for(int i = 0; i < input.length; i++) {
normalized[i] = (input[i] - means[i]) / scale[i];
}
return normalized;
}
private double[] concatenate(double[] a, double[] b) {
int size = a.length + b.length;
double[] concatArray = new double[size];
int index = 0;
for(double d : a) {
concatArray[index++] = d;
}
for(double d : b) {
concatArray[index++] = d;
}
return concatArray;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("hypothesis: ");
int i = 0;
sb.append(parameters[i++] + " + ");
for(; i < parameters.length-1; i++) {
sb.append(parameters[i] + "*x" + i + " + ");
}
sb.append(parameters[i] + "*x" + i);
sb.append("\n Feature scale: ");
for(i = 0; i < scale.length-1; i++) {
sb.append(scale[i] + " ");
}
sb.append(scale[i]);
sb.append("\n Feature means: ");
for(i = 0; i < means.length-1; i++) {
sb.append(means[i] + " ");
}
sb.append(means[i]);
sb.append("\n Cost fuction: " + costFunction());
return sb.toString();
}
public static void main(String[] args) {
final double[][] TDATA = {
//number of rooms, area, price
{2, 200, 200000},
{3, 300, 300000},
{4, 400, 400000},
{5, 500, 500000},
{8, 800, 800000},
{9, 900, 900000}
};
GradientDescent gd = new GradientDescent();
gd.setTrainingData(TDATA);
gd.setParameters(new double[]{0D, 0D, 0D});
gd.setLearningRate(0.1);
gd.train();
System.out.println(gd);
System.out.println("PREDICTION: " + gd.predict(new double[]{3, 600}));
}
}
最佳答案
看起来你的开始是合理的,但在将数学转换为代码时存在一些问题。请参阅以下数学。
我采取了几个步骤来阐明数学和算法的收敛机制。
在那之后,很明显在求和循环中除了缺少加号之外还有更多的错误。偏导数似乎需要重写或重大修改以匹配类(class)概念。
请注意,k=0->n 的内部循环生成所有特征的点积,然后在 i=0->m-1 循环中应用以解释每个训练案例。
所有这些都必须包含在每次迭代 r 中。该外循环的循环标准不应是某个最大 r 值。一旦收敛充分完成,您将需要满足一些标准。
针对评论的补充说明:
由于 Martin Fowler 所说的 Symantic Gap,很难发现代码中的不协调之处。在这种情况下,它介于三件事之间。
重构成员变量并从 x 矩阵中分离出 y vector (如下所示)很可能有助于发现不协调之处。
private int countMExamples;
private int countNFeatures;
private double[][] aX;
private double[] aY;
private double[] aMeans;
private double[] aScales;
private double[] aParamsTheta;
private double learnRate;
关于多元梯度下降的Java实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41144206/
背景: 我最近一直在使用 JPA,我为相当大的关系数据库项目生成持久层的轻松程度给我留下了深刻的印象。 我们公司使用大量非 SQL 数据库,特别是面向列的数据库。我对可能对这些数据库使用 JPA 有一
我已经在我的 maven pom 中添加了这些构建配置,因为我希望将 Apache Solr 依赖项与 Jar 捆绑在一起。否则我得到了 SolarServerException: ClassNotF
interface ITurtle { void Fight(); void EatPizza(); } interface ILeonardo : ITurtle {
我希望可用于 Java 的对象/关系映射 (ORM) 工具之一能够满足这些要求: 使用 JPA 或 native SQL 查询获取大量行并将其作为实体对象返回。 允许在行(实体)中进行迭代,并在对当前
好像没有,因为我有实现From for 的代码, 我可以转换 A到 B与 .into() , 但同样的事情不适用于 Vec .into()一个Vec . 要么我搞砸了阻止实现派生的事情,要么这不应该发
在 C# 中,如果 A 实现 IX 并且 B 继承自 A ,是否必然遵循 B 实现 IX?如果是,是因为 LSP 吗?之间有什么区别吗: 1. Interface IX; Class A : IX;
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我正在阅读标准haskell库的(^)的实现代码: (^) :: (Num a, Integral b) => a -> b -> a x0 ^ y0 | y0 a -> b ->a expo x0
我将把国际象棋游戏表示为 C++ 结构。我认为,最好的选择是树结构(因为在每个深度我们都有几个可能的移动)。 这是一个好的方法吗? struct TreeElement{ SomeMoveType
我正在为用户名数据库实现字符串匹配算法。我的方法采用现有的用户名数据库和用户想要的新用户名,然后检查用户名是否已被占用。如果采用该方法,则该方法应该返回带有数据库中未采用的数字的用户名。 例子: “贾
我正在尝试实现 Breadth-first search algorithm , 为了找到两个顶点之间的最短距离。我开发了一个 Queue 对象来保存和检索对象,并且我有一个二维数组来保存两个给定顶点
我目前正在 ika 中开发我的 Python 游戏,它使用 python 2.5 我决定为 AI 使用 A* 寻路。然而,我发现它对我的需要来说太慢了(3-4 个敌人可能会落后于游戏,但我想供应 4-
我正在寻找 Kademlia 的开源实现C/C++ 中的分布式哈希表。它必须是轻量级和跨平台的(win/linux/mac)。 它必须能够将信息发布到 DHT 并检索它。 最佳答案 OpenDHT是
我在一本书中读到这一行:-“当我们要求 C++ 实现运行程序时,它会通过调用此函数来实现。” 而且我想知道“C++ 实现”是什么意思或具体是什么。帮忙!? 最佳答案 “C++ 实现”是指编译器加上链接
我正在尝试使用分支定界的 C++ 实现这个背包问题。此网站上有一个 Java 版本:Implementing branch and bound for knapsack 我试图让我的 C++ 版本打印
在很多情况下,我需要在 C# 中访问合适的哈希算法,从重写 GetHashCode 到对数据执行快速比较/查找。 我发现 FNV 哈希是一种非常简单/好/快速的哈希算法。但是,我从未见过 C# 实现的
目录 LRU缓存替换策略 核心思想 不适用场景 算法基本实现 算法优化
1. 绪论 在前面文章中提到 空间直角坐标系相互转换 ,测绘坐标转换时,一般涉及到的情况是:两个直角坐标系的小角度转换。这个就是我们经常在测绘数据处理中,WGS-84坐标系、54北京坐标系
在软件开发过程中,有时候我们需要定时地检查数据库中的数据,并在发现新增数据时触发一个动作。为了实现这个需求,我们在 .Net 7 下进行一次简单的演示. PeriodicTimer .
二分查找 二分查找算法,说白了就是在有序的数组里面给予一个存在数组里面的值key,然后将其先和数组中间的比较,如果key大于中间值,进行下一次mid后面的比较,直到找到相等的,就可以得到它的位置。
我是一名优秀的程序员,十分优秀!