著者:タルン・グプタ
翻訳グループdeephub:孟Xiangjie
この記事では、勾配降下法を使用して(バッチ)線形回帰を実装する方法を学ぶためのpython3データ処理ライブラリで書かれたプログラムとしてnumpyのを使用するようになります。
私は徐々に動作原理とコードのコードの各部分の原理を説明します。
私たちは、勾配を計算し、この計算式を使用します。
ここで、x(i)は、Nはデータセットのサイズである点のベクトルです。N(ETA)は、当社の学習率です。Y(i)が目標出力ベクトルです。F(X)は、シグマは和関数である線形回帰関数のベクトルf(X)=合計(* X W)と定義されます。さらに、我々は最初の違いW0 = 0となるようにX0 = 1を検討します。すべての重みは0に初期化されています。
この方法では、我々は、二乗誤差損失関数の合計を使用しました。
SSEはゼロに初期化されていることを除いて、我々は、SSEの各反復の変化を記録し、プログラムの実行に先立って閾値と比較されます。SSEが閾値を下回る場合、プログラムは終了します。
このプログラムでは、コマンドラインから3つの入力を提供します。彼らは以下のとおりです。
-
しきい値 - しきい値は、アルゴリズムが終了する前に、損失がこのしきい値未満でなければなりません。
-
データ - 位置データセット。
-
learningRate - 学習率勾配降下方法。
そのため、プログラムの開始は、次のようになります。
python3 linearregr.py — data random.csv — learningRate 0.0001 — threshold 0.0001
次のように我々は最後のものを識別コードに入る前に、プログラムの出力は次のようになります。
iteration_number,weight0,weight1,weight2,...,weightN,sum_of_squared_errors
プログラムは6つの部分、私たちが見る一つ一つを含んでいます。
インポートモジュール
import argparse # to read inputs from command line
import csv # to read the input data set file
import numpy as np # to work with the data set
初期化部
# initialise argument parser and read arguments from command line with the respective flags and then call the main() functionif __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("-d", "--data", help="Data File")
parser.add_argument("-l", "--learningRate", help="Learning Rate")
parser.add_argument("-t", "--threshold", help="Threshold")
main()
主な機能の一部
def main():
args = parser.parse_args()
file, learningRate, threshold = args.data, float(
args.learningRate), float(args.threshold) # save respective command line inputs into variables
# read csv file and the last column is the target output and is separated from the input (X) as Y
with open(file) as csvFile:
reader = csv.reader(csvFile, delimiter=',')
X = []
Y = []
for row in reader:
X.append([1.0] + row[:-1])
Y.append([row[-1]])
# Convert data points into float and initialise weight vector with 0s.
n = len(X)
X = np.array(X).astype(float)
Y = np.array(Y).astype(float)
W = np.zeros(X.shape[1]).astype(float)
# this matrix is transposed to match the necessary matrix dimensions for calculating dot product
W = W.reshape(X.shape[1], 1).round(4)
# Calculate the predicted output value
f_x = calculatePredicatedValue(X, W)
# Calculate the initial SSE
sse_old = calculateSSE(Y, f_x)
outputFile = 'solution_' + \
'learningRate_' + str(learningRate) + '_threshold_' \
+ str(threshold) + '.csv'
'''
Output file is opened in writing mode and the data is written in the format mentioned in the post. After the
first values are written, the gradient and updated weights are calculated using the calculateGradient function.
An iteration variable is maintained to keep track on the number of times the batch linear regression is executed
before it falls below the threshold value. In the infinite while loop, the predicted output value is calculated
again and new SSE value is calculated. If the absolute difference between the older(SSE from previous iteration)
and newer(SSE from current iteration) SSE is greater than the threshold value, then above process is repeated.
The iteration is incremented by 1 and the current SSE is stored into previous SSE. If the absolute difference
between the older(SSE from previous iteration) and newer(SSE from current iteration) SSE falls below the
threshold value, the loop breaks and the last output values are written to the file.
'''
with open(outputFile, 'w', newline='') as csvFile:
writer = csv.writer(csvFile, delimiter=',', quoting=csv.QUOTE_NONE, escapechar='')
writer.writerow([*[0], *["{0:.4f}".format(val) for val in W.T[0]], *["{0:.4f}".format(sse_old)]])
gradient, W = calculateGradient(W, X, Y, f_x, learningRate)
iteration = 1
while True:
f_x = calculatePredicatedValue(X, W)
sse_new = calculateSSE(Y, f_x)
if abs(sse_new - sse_old) > threshold:
writer.writerow([*[iteration], *["{0:.4f}".format(val) for val in W.T[0]], *["{0:.4f}".format(sse_new)]])
gradient, W = calculateGradient(W, X, Y, f_x, learningRate)
iteration += 1
sse_old = sse_new
else:
break
writer.writerow([*[iteration], *["{0:.4f}".format(val) for val in W.T[0]], *["{0:.4f}".format(sse_new)]])
print("Output File Name: " + outputFile
次のようにプロセス主な機能は次のとおりです。
- 変数への入力に対応するコマンドライン
- CSVファイルを読み込む、最後のものが目標出力、および(Xとして格納されている)の入力と別Yとして記憶されます
- データ点は0の重みの浮動小数点初期化ベクトルに変換します
- 出力の予測値を計算するために使用calculatePredicatedValue機能
- SSE機能は初期calculateSSEを計算するために使用されます
- 出力ファイルを書き込みモードでオープンされ、データは記事で述べた形式で記述されています。最初の値を書き込み、calculateGradient更新重みを計算勾配機能を使用した後。反復の可変数が閾値未満の損失関数値の前に一括して行う線形回帰を決定するために行わ。無限ループで、再度予測出力値を算出し、SSEの新しい値を計算します。古い(SSEから前の反復)及び(現在の反復からSSE)との間の新しい絶対差が閾値より大きい場合、処理を繰り返します。反復1の数を増やし、SSEは現在、以前SSEに記憶されています。古い(SSEの前の反復)及び(SSEの現在の反復)との間の新しい絶対差がしきい値未満である場合、サイクルが中断され、ファイルに書き込まれ、最終的な出力値です。
calculatePredicatedValue
機能
ここで、生成物は、予測出力を実行することによって計算され、入力行列X Wは重み行列点です。
# dot product of X(input) and W(weights) as numpy matrices and returning the result which is the predicted output
def calculatePredicatedValue(X, W):
f_x = np.dot(X, W)
return f_x
calculateGradient
機能
勾配は、最初の記事に記載された式を用いて計算し、重みを更新します。
def calculateGradient(W, X, Y, f_x, learningRate):
gradient = (Y - f_x) * X
gradient = np.sum(gradient, axis=0)
temp = np.array(learningRate * gradient).reshape(W.shape)
W = W + temp
return gradient, W
calculateSSE
機能
SSEは、上記の式を用いて計算されます。
def calculateSSE(Y, f_x):
sse = np.sum(np.square(f_x - Y))
return sse
さて、完全なコードを読みます。プログラムの実施の結果で見てみましょう。
これは出力のようなものです:
0 |
0.0000 |
0.0000 |
0.0000 |
7475.3149 |
|
|
|
|
|
1 |
-0.0940 |
-0.5376 |
-0.2592 |
2111.5105 |
|
|
|
|
|
2 |
-0.1789 |
-0.7849 |
-0.3766 |
880.6980 |
|
|
|
|
|
3 |
-0.2555 |
-0.8988 |
-0.4296 |
538.8638 |
|
|
|
|
|
4 |
-0.3245 |
-0.9514 |
-0.4533 |
399.8092 |
|
|
|
|
|
5 |
-0.3867 |
-0.9758 |
-0.4637 |
316.1682 |
|
|
|
|
|
6 |
-0.4426 |
-0.9872 |
-0.4682 |
254.5126 |
|
|
|
|
|
7 |
-0.4930 |
-0.9926 |
-0.4699 |
205.8479 |
|
|
|
|
|
8 |
-0.5383 |
-0.9952 |
-0.4704 |
166.6932 |
|
|
|
|
|
9 |
-0.5791 |
-0.9966 |
-0.4704 |
135.0293 |
|
|
|
|
|
10 |
-0.6158 |
-0.9973 |
-0.4702 |
109.3892 |
|
|
|
|
|
11 |
-0.6489 |
-0.9978 |
-0.4700 |
88.6197 |
|
|
|
|
|
12 |
-0.6786 |
-0.9981 |
-0.4697 |
71.7941 |
|
|
|
|
|
13 |
-0.7054 |
-0.9983 |
-0.4694 |
58.1631 |
|
|
|
|
|
14 |
-0.7295 |
-0.9985 |
-0.4691 |
47.1201 |
|
|
|
|
|
15 |
-0.7512 |
-0.9987 |
-0.4689 |
38.1738 |
|
|
|
|
|
16 |
-0.7708 |
-0.9988 |
-0.4687 |
30.9261 |
|
|
|
|
|
17 |
-0.7883 |
-0.9989 |
-0.4685 |
25.0544 |
|
|
|
|
|
18 |
-0.8042 |
-0.9990 |
-0.4683 |
20.2975 |
|
|
|
|
|
19 |
-0.8184 |
-0.9991 |
-0.4681 |
16.4438 |
|
|
|
|
|
20 |
-0.8312 |
-0.9992 |
-0.4680 |
13.3218 |
|
|
|
|
|
21 |
-0.8427 |
-0.9993 |
-0.4678 |
10.7925 |
|
|
|
|
|
22 |
-0.8531 |
-0.9994 |
-0.4677 |
8.7434 |
|
|
|
|
|
23 |
-0.8625 |
-0.9994 |
-0.4676 |
7.0833 |
|
|
|
|
|
24 |
-0.8709 |
-0.9995 |
-0.4675 |
5.7385 |
|
|
|
|
|
25 |
-0.8785 |
-0.9995 |
-0.4674 |
4.6490 |
|
|
|
|
|
26 |
-0.8853 |
-0.9996 |
-0.4674 |
3.7663 |
|
|
|
|
|
27 |
-0.8914 |
-0.9996 |
-0.4673 |
3.0512 |
|
|
|
|
|
28 |
-0.8969 |
-0.9997 |
-0.4672 |
2.4719 |
|
|
|
|
|
29 |
-0.9019 |
-0.9997 |
-0.4672 |
2.0026 |
|
|
|
|
|
30 |
-0.9064 |
-0.9997 |
-0.4671 |
1.6224 |
|
|
|
|
|
31 |
-0.9104 |
-0.9998 |
-0.4671 |
1.3144 |
|
|
|
|
|
32 |
-0.9140 |
-0.9998 |
-0.4670 |
1.0648 |
|
|
|
|
|
33 |
-0.9173 |
-0.9998 |
-0.4670 |
0.8626 |
|
|
|
|
|
34 |
-0.9202 |
-0.9998 |
-0.4670 |
0.6989 |
|
|
|
|
|
35 |
-0.9229 |
-0.9998 |
-0.4669 |
0.5662 |
|
|
|
|
|
36 |
-0.9252 |
-0.9999 |
-0.4669 |
0.4587 |
|
|
|
|
|
37 |
-0.9274 |
-0.9999 |
-0.4669 |
0.3716 |
|
|
|
|
|
38 |
-0.9293 |
-0.9999 |
-0.4669 |
0.3010 |
|
|
|
|
|
39 |
-0.9310 |
-0.9999 |
-0.4668 |
0.2439 |
|
|
|
|
|
40 |
-0.9326 |
-0.9999 |
-0.4668 |
0.1976 |
|
|
|
|
|
41 |
-0.9340 |
-0.9999 |
-0.4668 |
0.1601 |
|
|
|
|
|
42 |
-0.9353 |
-0.9999 |
-0.4668 |
0.1297 |
|
|
|
|
|
43 |
-0.9364 |
-0.9999 |
-0.4668 |
0.1051 |
|
|
|
|
|
44 |
-0.9374 |
-0.9999 |
-0.4668 |
0.0851 |
|
|
|
|
|
45 |
-0.9384 |
-0.9999 |
-0.4668 |
0.0690 |
|
|
|
|
|
46 |
-0.9392 |
-0.9999 |
-0.4668 |
0.0559 |
|
|
|
|
|
47 |
-0.9399 |
-1.0000 |
-0.4667 |
0.0453 |
|
|
|
|
|
48 |
-0.9406 |
-1.0000 |
-0.4667 |
0.0367 |
|
|
|
|
|
49 |
-0.9412 |
-1.0000 |
-0.4667 |
0.0297 |
|
|
|
|
|
50 |
-0.9418 |
-1.0000 |
-0.4667 |
0.0241 |
|
|
|
|
|
51 |
-0.9423 |
-1.0000 |
-0.4667 |
0.0195 |
|
|
|
|
|
52 |
-0.9427 |
-1.0000 |
-0.4667 |
0.0158 |
|
|
|
|
|
53 |
-0.9431 |
-1.0000 |
-0.4667 |
0.0128 |
|
|
|
|
|
54 |
-0.9434 |
-1.0000 |
-0.4667 |
0.0104 |
|
|
|
|
|
55 |
-0.9438 |
-1.0000 |
-0.4667 |
0.0084 |
|
|
|
|
|
56 |
-0.9441 |
-1.0000 |
-0.4667 |
0.0068 |
|
|
|
|
|
57 |
-0.9443 |
-1.0000 |
-0.4667 |
0.0055 |
|
|
|
|
|
58 |
-0.9446 |
-1.0000 |
-0.4667 |
0.0045 |
|
|
|
|
|
59 |
-0.9448 |
-1.0000 |
-0.4667 |
0.0036 |
|
|
|
|
|
60 |
-0.9450 |
-1.0000 |
-0.4667 |
0.0029 |
|
|
|
|
|
61 |
-0.9451 |
-1.0000 |
-0.4667 |
0.0024 |
|
|
|
|
|
62 |
-0.9453 |
-1.0000 |
-0.4667 |
0.0019 |
|
|
|
|
|
63 |
-0.9454 |
-1.0000 |
-0.4667 |
0.0016 |
|
|
|
|
|
64 |
-0.9455 |
-1.0000 |
-0.4667 |
0.0013 |
|
|
|
|
|
65 |
-0.9457 |
-1.0000 |
-0.4667 |
0.0010 |
|
|
|
|
|
66 |
-0.9458 |
-1.0000 |
-0.4667 |
0.0008 |
|
|
|
|
|
67 |
-0.9458 |
-1.0000 |
-0.4667 |
0.0007 |
|
|
|
|
|
68 |
-0.9459 |
-1.0000 |
-0.4667 |
0.0005 |
|
|
|
|
|
69 |
-0.9460 |
-1.0000 |
-0.4667 |
0.0004 |
|
|
|
|
|
70 |
-0.9461 |
-1.0000 |
-0.4667 |
0.0004 |
|
|
|
|
|
####最终程序
import argparse
import csv
import numpy as np
def main():
args = parser.parse_args()
file, learningRate, threshold = args.data, float(
args.learningRate), float(args.threshold) # save respective command line inputs into variables
# read csv file and the last column is the target output and is separated from the input (X) as Y
with open(file) as csvFile:
reader = csv.reader(csvFile, delimiter=',')
X = []
Y = []
for row in reader:
X.append([1.0] + row[:-1])
Y.append([row[-1]])
# Convert data points into float and initialise weight vector with 0s.
n = len(X)
X = np.array(X).astype(float)
Y = np.array(Y).astype(float)
W = np.zeros(X.shape[1]).astype(float)
# this matrix is transposed to match the necessary matrix dimensions for calculating dot product
W = W.reshape(X.shape[1], 1).round(4)
# Calculate the predicted output value
f_x = calculatePredicatedValue(X, W)
# Calculate the initial SSE
sse_old = calculateSSE(Y, f_x)
outputFile = 'solution_' + \
'learningRate_' + str(learningRate) + '_threshold_' \
+ str(threshold) + '.csv'
'''
Output file is opened in writing mode and the data is written in the format mentioned in the post. After the
first values are written, the gradient and updated weights are calculated using the calculateGradient function.
An iteration variable is maintained to keep track on the number of times the batch linear regression is executed
before it falls below the threshold value. In the infinite while loop, the predicted output value is calculated
again and new SSE value is calculated. If the absolute difference between the older(SSE from previous iteration)
and newer(SSE from current iteration) SSE is greater than the threshold value, then above process is repeated.
The iteration is incremented by 1 and the current SSE is stored into previous SSE. If the absolute difference
between the older(SSE from previous iteration) and newer(SSE from current iteration) SSE falls below the
threshold value, the loop breaks and the last output values are written to the file.
'''
with open(outputFile, 'w', newline='') as csvFile:
writer = csv.writer(csvFile, delimiter=',', quoting=csv.QUOTE_NONE, escapechar='')
writer.writerow([*[0], *["{0:.4f}".format(val) for val in W.T[0]], *["{0:.4f}".format(sse_old)]])
gradient, W = calculateGradient(W, X, Y, f_x, learningRate)
iteration = 1
while True:
f_x = calculatePredicatedValue(X, W)
sse_new = calculateSSE(Y, f_x)
if abs(sse_new - sse_old) > threshold:
writer.writerow([*[iteration], *["{0:.4f}".format(val) for val in W.T[0]], *["{0:.4f}".format(sse_new)]])
gradient, W = calculateGradient(W, X, Y, f_x, learningRate)
iteration += 1
sse_old = sse_new
else:
break
writer.writerow([*[iteration], *["{0:.4f}".format(val) for val in W.T[0]], *["{0:.4f}".format(sse_new)]])
print("Output File Name: " + outputFile
def calculateGradient(W, X, Y, f_x, learningRate):
gradient = (Y - f_x) * X
gradient = np.sum(gradient, axis=0)
# gradient = np.array([float("{0:.4f}".format(val)) for val in gradient])
temp = np.array(learningRate * gradient).reshape(W.shape)
W = W + temp
return gradient, W
def calculateSSE(Y, f_x):
sse = np.sum(np.square(f_x - Y))
return sse
def calculatePredicatedValue(X, W):
f_x = np.dot(X, W)
return f_x
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("-d", "--data", help="Data File")
parser.add_argument("-l", "--learningRate", help="Learning Rate")
parser.add_argument("-t", "--threshold", help="Threshold")
main()
这篇文章介绍了使用梯度下降法进行批线性回归的数学概念。 在此,考虑了损失函数(在这种情况下为平方误差总和)。 我们没有看到最小化SSE的方法,而这是不应该的(需要调整学习率),我们看到了如何在阈值的帮助下使线性回归收敛。
该程序使用numpy来处理数据,也可以使用python的基础知识而不使用numpy来完成,但是它将需要嵌套循环,因此时间复杂度将增加到O(n * n)。 无论如何,numpy提供的数组和矩阵的内存效率更高。 另外,如果您喜欢使用pandas模块,建议您使用它,并尝试使用它来实现相同的程序。
希望您喜欢这篇文章。 谢谢阅读。
原文连接:https://imba.deephub.ai/p/1b9773506e2f11ea90cd05de3860c663