Add files via upload

This commit is contained in:
Iris Series: Visualize Math -- From Arithmetic Basics to Machine Learning
2025-02-01 17:08:33 +08:00
committed by GitHub
parent 79be5dda7d
commit 5adb9e44a7
27 changed files with 8126 additions and 0 deletions

View File

@@ -0,0 +1,242 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "73bd968b-d970-4a05-94ef-4e7abf990827",
"metadata": {},
"source": [
"Chapter 14\n",
"\n",
"# 矩阵平方根\n",
"Book_4《矩阵力量》 | 鸢尾花书:从加减乘除到机器学习 (第二版)"
]
},
{
"cell_type": "markdown",
"id": "c263fc95-881a-49fb-9c6c-a25508996623",
"metadata": {},
"source": [
"该代码的主要任务是通过矩阵分解和重构方法,基于给定的矩阵$A$,验证矩阵重构的正确性。具体流程如下:\n",
"\n",
"1. **定义矩阵$A$** \n",
" 代码首先创建了一个$2 \\times 2$矩阵 $A = \\begin{bmatrix} 1.25 & -0.75 \\\\ -0.75 & 1.25 \\end{bmatrix}$。矩阵 $A$ 是一个对称矩阵,因此可以进行特征值分解。\n",
"\n",
"2. **计算特征值和特征向量** \n",
" 代码使用 `np.linalg.eig` 函数对矩阵 $A$ 进行特征值分解,计算出$A$的特征值(存储在 $LAMBDA$ 中)和特征向量(存储在 $V$ 中),满足以下分解公式:\n",
" $$\n",
" A = V \\Lambda V^{-1}\n",
" $$\n",
" 其中,$\\Lambda$ 是一个包含特征值的对角矩阵,$V$ 是由特征向量组成的矩阵。\n",
"\n",
"3. **构建矩阵$B$** \n",
" 接下来,代码构建了一个新的矩阵 $B$。它的计算公式为:\n",
" $$\n",
" B = V \\sqrt{\\Lambda} V^{-1}\n",
" $$\n",
" 其中,$\\sqrt{\\Lambda}$ 是对角矩阵,其对角元素是 $\\Lambda$ 的平方根。也就是说,$B$ 是通过将 $A$ 的特征值取平方根后重新组合得到的矩阵。\n",
"\n",
"4. **重构矩阵$A$并验证结果** \n",
" 最后,通过矩阵 $B$ 构造了一个新矩阵 $A_{reproduced}$,其计算公式为:\n",
" $$\n",
" A_{reproduced} = B B^T\n",
" $$\n",
" 这是利用矩阵 $B$ 重构 $A$ 的过程。对称矩阵 $A$ 的特征值平方根分解使得 $B B^T$ 应等于原始矩阵 $A$。因此,通过打印 $A_{reproduced}$,可以验证 $B B^T$ 是否等于 $A$,从而确认重构的正确性。"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "2759881c-9e2a-4e4d-a79c-1617f2a4be4f",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np # 导入NumPy库"
]
},
{
"cell_type": "markdown",
"id": "a685835a-bcda-40f8-ac57-ff3738c0209f",
"metadata": {},
"source": [
"## 初始化矩阵A"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "a6e9a15c-1d77-4a95-a13c-1c825598e8be",
"metadata": {},
"outputs": [],
"source": [
"A = np.matrix([[1.25, -0.75], # 定义矩阵A\n",
" [-0.75, 1.25]]) # 矩阵的元素"
]
},
{
"cell_type": "markdown",
"id": "55297e89-757e-4136-a0af-4763d1db3e56",
"metadata": {},
"source": [
"## 计算特征值和特征向量"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "998fa920-5e90-408f-8b70-dd3633c870e9",
"metadata": {},
"outputs": [],
"source": [
"LAMBDA, V = np.linalg.eig(A) # 计算矩阵A的特征值LAMBDA和特征向量V"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "ce8e4f8c-19a0-4392-b1ef-e794dcc5f187",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([2. , 0.5])"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"LAMBDA"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "13196da8-54c0-44bb-ac29-6cf7c6fec408",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"matrix([[ 0.70710678, 0.70710678],\n",
" [-0.70710678, 0.70710678]])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"V"
]
},
{
"cell_type": "markdown",
"id": "c6bb1a72-57de-4ae9-a2e2-599db3122538",
"metadata": {},
"source": [
"## 构建矩阵B"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "cb92d4fe-8443-473f-a61c-378630468024",
"metadata": {},
"outputs": [],
"source": [
"B = V @ np.diag(np.sqrt(LAMBDA)) @ np.linalg.inv(V) # 根据特征值和特征向量构建矩阵B"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "da0f2576-d262-41cd-8324-208cf3df1c82",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"matrix([[ 1.06066017, -0.35355339],\n",
" [-0.35355339, 1.06066017]])"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"B"
]
},
{
"cell_type": "markdown",
"id": "d70a4893-0524-47aa-91ad-4ad9863a5c89",
"metadata": {},
"source": [
"## 重构矩阵A并打印"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "d07c6472-787a-44c2-9d20-99e4d070826a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[[ 1.25 -0.75]\n",
" [-0.75 1.25]]\n"
]
}
],
"source": [
"A_reproduced = B @ B.T # 通过矩阵B的转置乘积重构矩阵A\n",
"print(A_reproduced) # 输出重构后的矩阵A"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "85a80909-2aac-49ed-bb7a-f8cc6b80ee7d",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "ecd322f4-f919-4be2-adc3-69d28ef25e69",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,54 @@
###############
# Authored by Weisheng Jiang
# Book 4 | From Basic Arithmetic to Machine Learning
# Published and copyrighted by Tsinghua University Press
# Beijing, China, 2025
###############
import numpy as np # 导入NumPy库用于数值计算
import streamlit as st # 导入Streamlit库用于创建交互式Web应用
import time # 导入time模块用于添加延时效果
# 定义状态转移矩阵 A
A = np.matrix([[0.7, 0.2], # 第一行表示从鸡到鸡的概率为0.7从鸡到兔子的概率为0.3
[0.3, 0.8]]) # 第二行表示从兔子到鸡的概率为0.2从兔子到兔子的概率为0.8
# 在侧边栏中创建交互组件
with st.sidebar:
# 创建滑块,用于用户设置初始状态中鸡的比例
pi_0_chicken = st.slider('Ratio of chicken:', # 滑块标题
0.0, 1.0, step=0.1) # 范围从0到1每次步进0.1
pi_0_rabbit = 1 - pi_0_chicken # 计算兔子的比例使鸡和兔子的比例和为1
st.write('Ratio of rabbit: ' + str(round(pi_0_rabbit, 1))) # 显示兔子的比例保留1位小数
# 创建滑块,用于用户设置模拟的天数
num_iterations = st.slider('Number of nights:', # 滑块标题
20, 100, step=5) # 范围从20到100每次步进5
# 创建进度条和状态文本,用于显示迭代进度
progress_bar = st.sidebar.progress(0) # 初始化进度条为0%
status_text = st.sidebar.empty() # 创建一个空白的文本区域,用于显示进度百分比
# 初始化状态向量,将用户输入的初始比例存入数组
last_rows = np.array([[pi_0_chicken, pi_0_rabbit]]) # 初始状态为一个行向量,包含鸡和兔子的比例
# 创建一个折线图,用于实时展示状态变化
chart = st.line_chart(last_rows) # 初始化折线图,并将初始状态绘制到图上
# 开始迭代模拟状态转移
for i in range(1, num_iterations): # 循环从第1天到用户设置的总天数
last_status = last_rows[-1, :] # 获取当前的状态向量(最后一行)
new_rows = last_status @ A.T # 使用矩阵乘法计算下一个状态,转移矩阵取转置
percent = (i + 1) * 100 / num_iterations # 计算当前完成的百分比
# 更新进度条和状态文本
status_text.text("%i%% Complete" % percent) # 显示当前完成的百分比
chart.add_rows(new_rows) # 将新状态添加到折线图
progress_bar.progress(i) # 更新进度条的值
last_rows = new_rows # 更新最后的状态向量为当前计算的状态
time.sleep(0.1) # 延时0.1秒,以便观察每次状态更新
# 清空进度条,表示模拟完成
progress_bar.empty() # 移除进度条

View File

@@ -0,0 +1,119 @@
###############
# Authored by Weisheng Jiang
# Book 4 | From Basic Arithmetic to Machine Learning
# Published and copyrighted by Tsinghua University Press
# Beijing, China, 2025
###############
import plotly.graph_objects as go # 导入 Plotly 的图形对象模块,用于创建复杂的图形
import streamlit as st # 导入 Streamlit 库,用于创建交互式 Web 应用
import numpy as np # 导入 NumPy用于数值计算
import plotly.express as px # 导入 Plotly Express用于快速绘制图表
import pandas as pd # 导入 Pandas用于数据处理
import sympy # 导入 SymPy用于符号运算和公式化表达
# 定义函数 bmatrix将 NumPy 数组转换为 LaTeX 格式的矩阵表示
def bmatrix(a):
"""返回一个 LaTeX 矩阵表示"""
if len(a.shape) > 2: # 检查输入是否为二维数组
raise ValueError('bmatrix 函数最多显示二维矩阵') # 如果不是二维,抛出异常
lines = str(a).replace('[', '').replace(']', '').splitlines() # 将数组转换为字符串并去掉方括号
rv = [r'\begin{bmatrix}'] # 开始 LaTeX 矩阵的格式
rv += [' ' + ' & '.join(l.split()) + r'\\' for l in lines] # 逐行添加 LaTeX 矩阵行
rv += [r'\end{bmatrix}'] # 结束 LaTeX 矩阵的格式
return '\n'.join(rv) # 返回拼接后的 LaTeX 字符串
# 创建 Streamlit 侧边栏,用于调整矩阵 A 的参数
with st.sidebar:
# 显示矩阵 A 的 LaTeX 表示
st.latex(r'''
A = \begin{bmatrix}
a & b\\
b & c
\end{bmatrix}''')
# 创建滑块,允许用户调整矩阵 A 的元素 a, b, c 的值
a = st.slider('a', -2.0, 2.0, step=0.05, value=1.0) # 滑块用于调整 a 的值
b = st.slider('b', -2.0, 2.0, step=0.05, value=0.0) # 滑块用于调整 b 的值
c = st.slider('c', -2.0, 2.0, step=0.05, value=1.0) # 滑块用于调整 c 的值
#%% 创建一个单位圆的点集
theta_array = np.linspace(0, 2 * np.pi, 36) # 在 [0, 2π] 区间生成 36 个点,表示角度
X = np.column_stack((np.cos(theta_array), # 用 cos 和 sin 创建单位圆上的点
np.sin(theta_array)))
# 创建矩阵 A
A = np.array([[a, b], # 矩阵 A 的第一行
[b, c]]) # 矩阵 A 的第二行
# 显示单位圆的方程和线性变换后的方程
st.latex(r'''z^Tz = 1''') # 显示单位圆的方程
st.latex(r'''x = Az''') # 显示线性变换的方程
# 显示矩阵 A 的 LaTeX 表示
st.latex('A =' + bmatrix(A))
# 对单位圆的点集进行线性变换
X_ = X @ A # 对单位圆上的点集 X 应用线性变换矩阵 A
#%% 使用符号运算求解椭圆的方程
x1, x2 = sympy.symbols('x1 x2') # 定义符号变量 x1 和 x2
y1, y2 = sympy.symbols('y1 y2') # 定义符号变量 y1 和 y2
x = np.array([[x1, x2]]).T # 定义符号向量 x
y = np.array([[y1, y2]]).T # 定义符号向量 y
# 计算 Q 矩阵
Q = np.linalg.inv(A @ A.T) # Q = (AA^T)^(-1)
D, V = np.linalg.eig(Q) # 计算 Q 的特征值和特征向量
D = np.diag(D) # 将特征值转化为对角矩阵
# 显示 Q 矩阵的分解
st.latex(r'Q = \left( AA^T\right)^{-1} = ' + bmatrix(np.round(Q, 3))) # 显示 Q 矩阵
st.latex(r'''Q = V \Lambda V^{T}''') # 显示特征分解公式
st.latex(bmatrix(np.around(Q, decimals=3)) + '=' +
bmatrix(np.around(V, decimals=3)) + '@' +
bmatrix(np.around(D, decimals=3)) + '@' +
bmatrix(np.around(V.T, decimals=3))) # 显示分解过程
# 定义单位圆和变换后的椭圆方程
f_x = x.T @ np.round(Q, 3) @ x # 单位圆在 Q 矩阵下的方程
f_y = y.T @ np.round(D, 3) @ y # 椭圆在对角矩阵 D 下的方程
# 显示椭圆方程
from sympy import *
st.write('The formula of the ellipse:') # 显示椭圆方程的标题
st.latex(latex(simplify(f_x[0][0])) + ' = 1') # 显示椭圆方程
st.write('The formula of the transformed ellipse:') # 显示变换后椭圆方程的标题
st.latex(latex(simplify(f_y[0][0])) + ' = 1') # 显示变换后的椭圆方程
#%% 添加颜色信息到变换后的点集
color_array = np.linspace(0, 1, len(X)) # 为每个点生成一个颜色值
X_c = np.column_stack((X_, color_array)) # 将颜色信息添加到点集中
df = pd.DataFrame(X_c, columns=['x1', 'x2', 'color']) # 将点集转换为 DataFrame 格式
#%% 绘制散点图
fig = px.scatter(df, # 使用 Pandas 数据框作为数据源
x="x1", # 横轴为 x1
y="x2", # 纵轴为 x2
color='color', # 根据颜色值为点上色
color_continuous_scale=px.colors.sequential.Rainbow) # 使用彩虹色带
# 设置图形布局
fig.update_layout(
autosize=False, # 禁用自动调整尺寸
width=500, # 图表宽度为 500 像素
height=500) # 图表高度为 500 像素
# 添加横轴和纵轴的参考线
fig.add_hline(y=0, line_color='black') # 添加黑色的水平参考线
fig.add_vline(x=0, line_color='black') # 添加黑色的垂直参考线
fig.update_layout(coloraxis_showscale=False) # 隐藏颜色条
fig.update_xaxes(range=[-3, 3]) # 设置 x 轴范围
fig.update_yaxes(range=[-3, 3]) # 设置 y 轴范围
# 在 Streamlit 页面上显示图表
st.plotly_chart(fig)