In [ ]:
# 任意选一个你喜欢的整数,这能帮你得到稳定的结果
seed = 1234
In [ ]:
# 这个项目设计来帮你熟悉 python list 和线性代数
# 你不能调用任何NumPy以及相关的科学计算库来完成作业
# 本项目要求矩阵统一使用二维列表表示,如下:
A = [[1,2,3],
[2,3,3],
[1,2,5]]
B = [[1,2,3,5],
[2,3,3,5],
[1,2,5,1]]
# 向量也用二维列表表示
C = [[1],
[2],
[3]]
#TODO 创建一个 4*4 单位矩阵
I = [[1,0,0,0],
[0,1,0,0],
[0,0,1,0],
[0,0,0,1]]
In [ ]:
# 运行以下代码测试你的 shape 函数
%run -i -e test.py LinearRegressionTestCase.test_shape
In [ ]:
# TODO 返回矩阵的行数和列数
def shape(M):
return len(M),len(M[0])
In [ ]:
# TODO 每个元素四舍五入到特定小数数位
# 直接修改参数矩阵,无返回值
def matxRound(M, decPts=4):
row, col = shape(M)
for i in range(row):
for j in range(col):
M[i][j]=round(M[i][j],decPts)
pass
In [ ]:
# 运行以下代码测试你的 matxRound 函数
%run -i -e test.py LinearRegressionTestCase.test_matxRound
In [ ]:
# TODO 计算矩阵的转置
def transpose(M):
row, col = shape(M)
MT = []
for i in range(col):
MT.append([x[i] for x in M])
return MT
In [ ]:
# 运行以下代码测试你的 transpose 函数
%run -i -e test.py LinearRegressionTestCase.test_transpose
In [ ]:
# TODO 计算矩阵乘法 AB,如果无法相乘则raise ValueError
def matxMultiply(A, B):
rowA, colA = shape(A)
rowB, colB = shape(B)
if not colA == rowB:
raise ValueError
# result would be rowA x colB
result = [[0] * colB for row in range(rowA)]
BT = transpose(B)
for i in range(rowA):
rowa = A[i]
for j in range(colB):
colb = BT[j]
element = sum([rowa[x]*colb[x] for x in range(colA)])
result[i][j] = element
return result
In [ ]:
# 运行以下代码测试你的 matxMultiply 函数
%run -i -e test.py LinearRegressionTestCase.test_matxMultiply
$ A = \begin{bmatrix} a_{11} & a_{12} & ... & a_{1n}\\ a_{21} & a_{22} & ... & a_{2n}\\ a_{31} & a_{22} & ... & a_{3n}\\ ... & ... & ... & ...\\ a_{n1} & a_{n2} & ... & a_{nn}\\ \end{bmatrix} , b = \begin{bmatrix} b_{1} \\ b_{2} \\ b_{3} \\ ... \\ b_{n} \\ \end{bmatrix}$
返回 $ Ab = \begin{bmatrix} a_{11} & a_{12} & ... & a_{1n} & b_{1}\\ a_{21} & a_{22} & ... & a_{2n} & b_{2}\\ a_{31} & a_{22} & ... & a_{3n} & b_{3}\\ ... & ... & ... & ...& ...\\ a_{n1} & a_{n2} & ... & a_{nn} & b_{n} \end{bmatrix}$
In [ ]:
# TODO 构造增广矩阵,假设A,b行数相同
def augmentMatrix(A, b):
# result would be rowA x (colA+colb)
rowA, colA = shape(A)
result = [[0] * (colA+1) for row in range(rowA)]
for i in range(rowA):
for j in range(colA):
result[i][j] = A[i][j]
result[i][colA] = b[i][0]
return result
In [ ]:
# 运行以下代码测试你的 augmentMatrix 函数
%run -i -e test.py LinearRegressionTestCase.test_augmentMatrix
In [ ]:
# TODO r1 <---> r2
# 直接修改参数矩阵,无返回值
def swapRows(M, r1, r2):
colM = shape(M)[1]
for i in range(colM):
tmp = M[r1][i]
M[r1][i] = M[r2][i]
M[r2][i] = tmp
pass
In [ ]:
# 运行以下代码测试你的 swapRows 函数
%run -i -e test.py LinearRegressionTestCase.test_swapRows
In [ ]:
# TODO r1 <--- r1 * scale
# scale为0是非法输入,要求 raise ValueError
# 直接修改参数矩阵,无返回值
def scaleRow(M, r, scale):
if scale == 0:
raise ValueError
colM = shape(M)[1]
for i in range(colM):
M[r][i] *= scale
pass
In [ ]:
# 运行以下代码测试你的 scaleRow 函数
%run -i -e test.py LinearRegressionTestCase.test_scaleRow
In [ ]:
# TODO r1 <--- r1 + r2*scale
# 直接修改参数矩阵,无返回值
def addScaledRow(M, r1, r2, scale):
colM = shape(M)[1]
for i in range(colM):
M[r1][i] += M[r2][i]*scale
pass
In [ ]:
# 运行以下代码测试你的 addScaledRow 函数
%run -i -e test.py LinearRegressionTestCase.test_addScaledRow
步骤1 检查A,b是否行数相同
步骤2 构造增广矩阵Ab
步骤3 逐列转换Ab为化简行阶梯形矩阵 中文维基链接
对于Ab的每一列(最后一列除外)
当前列为列c
寻找列c中 对角线以及对角线以下所有元素(行 c~N)的绝对值的最大值
如果绝对值最大值为0
那么A为奇异矩阵,返回None (你可以在选做问题2.4中证明为什么这里A一定是奇异矩阵)
否则
使用第一个行变换,将绝对值最大值所在行交换到对角线元素所在行(行c)
使用第二个行变换,将列c的对角线元素缩放为1
多次使用第三个行变换,将列c的其他元素消为0
步骤4 返回Ab的最后一列
注: 我们并没有按照常规方法先把矩阵转化为行阶梯形矩阵,再转换为化简行阶梯形矩阵,而是一步到位。如果你熟悉常规方法的话,可以思考一下两者的等价性。
$Ab = \begin{bmatrix} -7 & 5 & -1 & 1\\ 1 & -3 & -8 & 1\\ -10 & -2 & 9 & 1\end{bmatrix}$
$ --> $ $\begin{bmatrix} 1 & \frac{1}{5} & -\frac{9}{10} & -\frac{1}{10}\\ 0 & -\frac{16}{5} & -\frac{71}{10} & \frac{11}{10}\\ 0 & \frac{32}{5} & -\frac{73}{10} & \frac{3}{10}\end{bmatrix}$
$ --> $ $\begin{bmatrix} 1 & 0 & -\frac{43}{64} & -\frac{7}{64}\\ 0 & 1 & -\frac{73}{64} & \frac{3}{64}\\ 0 & 0 & -\frac{43}{4} & \frac{5}{4}\end{bmatrix}$
$ --> $ $\begin{bmatrix} 1 & 0 & 0 & -\frac{3}{16}\\ 0 & 1 & 0 & -\frac{59}{688}\\ 0 & 0 & 1 & -\frac{5}{43}\end{bmatrix}$
gj_Solve
你可以用python的 fractions 模块辅助你的约分
In [ ]:
# 不要修改这里!
from helper import *
A = generateMatrix(3,seed,singular=False)
b = np.ones(shape=(3,1),dtype=int) # it doesn't matter
Ab = augmentMatrix(A.tolist(),b.tolist()) # 请确保你的增广矩阵已经写好了
printInMatrixFormat(Ab,padding=3,truncating=0)
请按照算法的步骤3,逐步推演可逆矩阵的变换。
在下面列出每一次循环体执行之后的增广矩阵。
要求:
\frac{n}{m}
来渲染分数,如下:$ Ab = \begin{bmatrix} 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \end{bmatrix}$
$ --> \begin{bmatrix} 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \end{bmatrix}$
$ --> \begin{bmatrix} 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \end{bmatrix}$
$...$
In [ ]:
# 不要修改这里!
A = generateMatrix(3,seed,singular=True)
b = np.ones(shape=(3,1),dtype=int)
Ab = augmentMatrix(A.tolist(),b.tolist()) # 请确保你的增广矩阵已经写好了
printInMatrixFormat(Ab,padding=3,truncating=0)
请按照算法的步骤3,逐步推演奇异矩阵的变换。
在下面列出每一次循环体执行之后的增广矩阵。
要求:
\frac{n}{m}
来渲染分数,如下:$ Ab = \begin{bmatrix} 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \end{bmatrix}$
$ --> \begin{bmatrix} 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \end{bmatrix}$
$ --> \begin{bmatrix} 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \end{bmatrix}$
$...$
In [ ]:
# TODO 实现 Gaussain Jordan 方法求解 Ax = b
""" Gaussian Jordan 方法求解 Ax = b.
参数
A: 方阵
b: 列向量
decPts: 四舍五入位数,默认为4
epsilon: 判读是否为0的阈值,默认 1.0e-16
返回列向量 x 使得 Ax = b
返回None,如果 A,b 高度不同
返回None,如果 A 为奇异矩阵
"""
from fractions import Fraction
def gj_Solve(A, b, decPts=4, epsilon = 1.0e-16):
def max_idx(list):
if max(list)<=epsilon:
raise ValueError
return 0 if len(list)<=0 else list.index(max(list))
if not shape(A)[0] == shape(b)[0]:
return None
Ab = augmentMatrix(A, b)
for i in range(shape(A)[1]):
col_i = [abs(Ab[row_num][i]) for row_num in range(i, shape(Ab)[0])]
try:
idx = max_idx(col_i) + i
swapRows(Ab, i, idx)
scaleRow(Ab, i, 1.0/Ab[i][i])
for j in range(shape(Ab)[0]):
if j != i:
addScaledRow(Ab, j, i, Fraction(-Ab[j][i]))
except ValueError:
return None
result = [[0] * 1 for row in range(shape(Ab)[0])]
for i in range(shape(Ab)[0]):
result[i][0]=Ab[i][-1]
return result
In [ ]:
# 运行以下代码测试你的 gj_Solve 函数
%run -i -e test.py LinearRegressionTestCase.test_gj_Solve
TODO 证明:
In [ ]:
# 不要修改这里!
# 运行一次就够了!
from helper import *
from matplotlib import pyplot as plt
%matplotlib inline
X,Y = generatePoints(seed,num=100)
## 可视化
plt.xlim((-5,5))
plt.xlabel('x',fontsize=18)
plt.ylabel('y',fontsize=18)
plt.scatter(X,Y,c='b')
plt.show()
In [ ]:
#TODO 请选择最适合的直线 y = mx + b
m1 = 3.2
b1 = 7.2
# 不要修改这里!
plt.xlim((-5,5))
x_vals = plt.axes().get_xlim()
y_vals = [m1*x+b1 for x in x_vals]
plt.plot(x_vals, y_vals, '-', color='r')
plt.xlabel('x',fontsize=18)
plt.ylabel('y',fontsize=18)
plt.scatter(X,Y,c='b')
plt.show()
我们要编程计算所选直线的平均平方误差(MSE), 即数据集中每个点到直线的Y方向距离的平方的平均数,表达式如下: $$ MSE = \frac{1}{n}\sum_{i=1}^{n}{(y_i - mx_i - b)^2} $$
In [ ]:
# TODO 实现以下函数并输出所选直线的MSE
def calculateMSE(X,Y,m,b):
list_ = ([(val[1]-val[0]*m-b)**2 for val in zip(X,Y)])
return sum(list_)/len(list_)
print(calculateMSE(X,Y,m1,b1))
这一部分需要简单的微积分知识( $ (x^2)' = 2x $ )。因为这是一个线性代数项目,所以设为选做。
刚刚我们手动调节参数,尝试找到最小的平方平均误差。下面我们要精确得求解 $m, b$ 使得平方平均误差最小。
定义目标函数 $E$ 为 $$ E = \frac{1}{2}\sum_{i=1}^{n}{(y_i - mx_i - b)^2} $$
因为 $E = \frac{n}{2}MSE$, 所以 $E$ 取到最小值时,$MSE$ 也取到最小值。要找到 $E$ 的最小值,即要找到 $m, b$ 使得 $E$ 相对于 $m$, $E$ 相对于 $b$ 的偏导数等于0.
因此我们要解下面的方程组。
$$ \begin{cases} \displaystyle \frac{\partial E}{\partial m} =0 \\ \\ \displaystyle \frac{\partial E}{\partial b} =0 \\ \end{cases} $$首先我们计算两个式子左边的值
证明/计算: $$ \frac{\partial E}{\partial m} = \sum_{i=1}^{n}{-x_i(y_i - mx_i - b)} $$
$$ \frac{\partial E}{\partial b} = \sum_{i=1}^{n}{-(y_i - mx_i - b)} $$TODO 证明:
TODO 写出目标函数,方程组和最优参数
我们的二元二次方程组可以用更简洁的矩阵形式表达,将方程组写成矩阵形式更有利于我们使用 Gaussian Jordan 消元法求解。
请证明 $$ \begin{bmatrix} \frac{\partial E}{\partial m} \\ \frac{\partial E}{\partial b} \end{bmatrix} = X^TXh - X^TY $$
其中向量 $Y$, 矩阵 $X$ 和 向量 $h$ 分别为 : $$ Y = \begin{bmatrix} y_1 \\ y_2 \\ ... \\ y_n \end{bmatrix} , X = \begin{bmatrix} x_1 & 1 \\ x_2 & 1\\ ... & ...\\ x_n & 1 \\ \end{bmatrix}, h = \begin{bmatrix} m \\ b \\ \end{bmatrix} $$
TODO 证明:
In [ ]:
# TODO 实现线性回归
'''
参数:X, Y 存储着一一对应的横坐标与纵坐标的两个一维数组
返回:m,b 浮点数
'''
def linearRegression(X,Y):
MX = [[val,1] for val in X]
MXT = transpose(MX)
result_left = matxMultiply(MXT,MX)
MY = [[val] for val in Y]
result_right = matxMultiply(MXT,MY)
[[m],[b]]=gj_Solve(result_left,result_right)
return (m,b)
m2,b2 = linearRegression(X,Y)
assert isinstance(m2,float),"m is not a float"
assert isinstance(b2,float),"b is not a float"
print(m2,b2)
你求得的回归结果是什么? 请使用运行以下代码将它画出来。
In [ ]:
# 请不要修改下面的代码
x1,x2 = -5,5
y1,y2 = x1*m2+b2, x2*m2+b2
plt.xlim((-5,5))
plt.xlabel('x',fontsize=18)
plt.ylabel('y',fontsize=18)
plt.scatter(X,Y,c='b')
plt.plot((x1,x2),(y1,y2),'r')
plt.title('y = {m:.4f}x + {b:.4f}'.format(m=m2,b=b2))
plt.show()
你求得的回归结果对当前数据集的MSE是多少?
In [ ]:
print(calculateMSE(X,Y,m2,b2))