最新文章
-
Windows Ai开发环境安装
annaconda可以理解为ai环境可以创建很多个房间,比如允许多个不同版本的python。每个房间可以保存不同的环境变量。 步骤1:下载安装包,安装anaconda,https://www.anaconda.com/ 步骤2:设置环境变量 设置环境变量需要根据软件实际的安装位置,这里的软件是安装的D盘的。在cmd命令中,执行conda info表示设置环境变量成功。 步骤3: 创建环境 打开Anaconda Prompt终端界面,创建开发环境前,先更新清华的源。 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/linux-64/ conda config --set show_channel_urls yes 然后进行安装: conda create -n py39_test python=3.9 -y 其中-n指定环境的名称, python=3.9表示安装python3.9的版本,-y表示同意所有安装过程中的所有确认。 步骤4: 激活环境 conda activate py39_test 步骤4:安装基础环境 pip install -r requirements.txt 使用pip install 进行安装,requirements.txt内容如下。 contourpy==1.3.0 cycler==0.12.1 filelock==3.16.1 fonttools==4.55.3 fsspec==2024.12.0 importlib_resources==6.5.2 Jinja2==3.1.5 kiwisolver==1.4.7 MarkupSafe==3.0.2 matplotlib==3.9.4 mpmath==1.3.0 networkx==3.2.1 numpy==2.0.2 packaging==24.2 pandas==2.2.3 pillow==11.1.0 pyparsing==3.2.1 python-dateutil==2.9.0.post0 pytz==2024.2 six==1.17.0 sympy==1.13.1 torch==2.5.1 torchaudio==2.5.1 torchvision==0.20.1 typing_extensions==4.12.2 tzdata==2024.2 zipp==3.21.0 使用pip list可以查看安装的包。 步骤5:安装pycharm,下载链接 https://www.jetbrains.com/pycharm/download/?section=windows -
前向传播、反向传播和计算图
前向传播(Forward Propagation) 前向传播是神经网络中从输入数据到输出预测值的计算过程。它通过逐层应用权重(W)和偏置(b),最终生成预测值 $y' $,并计算损失函数$L $。 模型定义 $$ y' = W \cdot x + b $$ 损失函数(均方误差) $$ L = \frac{1}{n} \sum_{i=1}^{n} (y'(i) - y_{\text{true}}(i))^2 $$ 示例 输入数据:$x = [1.0, 2.0] $ 真实标签:$y_{\text{true}} = [3.0, 5.0]$ 参数初始值:$W = 1.0, \, b = 0.5$ 前向计算 预测值:$y'(1) = 1.0 \cdot 1.0 + 0.5 = 1.5, \quad y'(2) = 1.0 \cdot 2.0 + 0.5 = 2.5$ 损失函数:$L = \frac{1}{2} \left[ (1.5 - 3)^2 + (2.5 - 5)^2 \right] = \frac{1}{2} (2.25 + 6.25) = 4.25$ 计算图(Computational Graph) 计算图是一种数据结构,用于表示前向传播中的计算过程。图中的节点代表数学操作(如加法、乘法),边代表数据流动(张量)。 对上述线性回归模型,计算图如下: 输入 x → (Multiply W) → (Add b) → 预测值 t_p → (Subtract y_true) → 误差平方 → 求和平均 → 损失 L 节点:乘法、加法、平方、求和、平均等操作。 边:数据流(如 $ x, W, b, y', L$)。 反向传播(Backward Propagation) 反向传播是通过链式法则(Chain Rule),从损失函数$ L $开始,反向计算每个参数$(W, b)$的梯度 $( \frac{\partial L}{\partial W} ) $和$ ( \frac{\partial L}{\partial b} ) $的过程。下面以线性回归模型公式示例: 损失对预测值的梯度 $$ \frac{\partial L}{\partial y'(i)} = \frac{2}{n} (y'(i) - y_{\text{true}}(i)) $$ 预测值对参数的梯度 对权重 $W $:$ \frac{\partial y'(i)}{\partial W} = x(i) $ 对偏置 $b $:$\frac{\partial y'(i)}{\partial b} = 1 $ 合并梯度 权重梯度:$ \frac{\partial L}{\partial W} = \sum_{i=1}^{n} \frac{\partial L}{\partial y'(i)} \cdot \frac{\partial y'(i)}{\partial W} = \frac{2}{n} \sum_{i=1}^{n} (y'(i) - y_{\text{true}}(i)) \cdot x(i) $ 偏置梯度:$ \frac{\partial L}{\partial b} = \sum_{i=1}^{n} \frac{\partial L}{\partial y'(i)} \cdot \frac{\partial y'(i)}{\partial b} = \frac{2}{n} \sum_{i=1}^{n} (y'(i) - y_{\text{true}}(i)) $ 反向传播示例 使用前向传播的结果: 计算误差项 $$ y'(1) - y_{\text{true}}(1) = 1.5 - 3 = -1.5, \quad y'(2) - y_{\text{true}}(2) = 2.5 - 5 = -2.5 $$ 计算梯度 权重梯度:$\frac{\partial L}{\partial W} = \frac{2}{2} [(-1.5) \cdot 1.0 + (-2.5) \cdot 2.0] = 1.0 \cdot (-1.5 - 5) = -6.5 $ 偏置梯度:$\frac{\partial L}{\partial b} = \frac{2}{2} [(-1.5) + (-2.5)] = 1.0 \cdot (-4) = -4$ PyTorch示例 import torch # 定义参数(启用梯度追踪) W = torch.tensor(1.0, requires_grad=True) b = torch.tensor(0.5, requires_grad=True) # 输入数据 x = torch.tensor([1.0, 2.0]) y_true = torch.tensor([3.0, 5.0]) # 前向传播 y_pred = W * x + b loss = torch.mean((y_pred - y_true) ** 2) # 反向传播 loss.backward() # 输出梯度 print(f"dL/dW: {W.grad}") # 输出 tensor(-6.5) print(f"dL/db: {b.grad}") # 输出 tensor(-4.0) 关键点说明 动态计算图:PyTorch 在前向传播时自动构建计算图。 反向传播触发:调用 .backward() 后,从损失节点反向遍历图,计算所有 requires_grad=True 的张量的梯度。 梯度存储:梯度结果存储在张量的 .grad 属性中。 总结: 概念 作用 示例中的体现 前向传播 计算预测值和损失函数 $ y' = W \cdot x + b, L = 4.25 $ 计算图 记录所有计算操作,为反向传播提供路径 乘法、加法、平方、求和、平均等操作组成的数据结构 反向传播 通过链式法则计算参数梯度 $ \frac{\partial L}{\partial W} = -6.5 $ 输入 x │ ▼ [W*x] → 乘法操作(计算图节点) │ ▼ [+b] → 加法操作(计算图节点) │ ▼ 预测值 y' → [平方损失] → 平均损失 L │ ▲ └──────────────────────────┘ 反向传播(梯度回传) -
梯度计算
什么是梯度 梯度(Gradient)是用于描述多元函数在某一点的变化率最大的方向及其大小。在深度学习中,梯度被广泛用于优化模型参数(如神经网络的权重和偏置),通过梯度下降等算法最小化损失函数。 对于多元函数 $f(x_1, x_2, \dots, x_n)$,其梯度是一个向量,由函数对每个变量的偏导数组成,记作: $$ \nabla f = \left( \frac{\partial f}{\partial x_1}, \frac{\partial f}{\partial x_2}, \dots, \frac{\partial f}{\partial x_n} \right) $$ 其中: $\nabla f$ 是梯度符号(读作“nabla f”)。 $\frac{\partial f}{\partial x_i}$ 是函数 $f$ 对变量 $x_i$ 的偏导数。 直观理解梯度 假设有一个二元函数 $f(x, y) = x^2 + y^2$,其梯度为: $$ \nabla f = \left( \frac{\partial f}{\partial x}, \frac{\partial f}{\partial y} \right) = (2x, 2y) $$ 在点 $(1, 1)$ 处,梯度为 $(2, 2)$,表示函数在该点沿方向 $(2, 2)$ 增长最快。 若想最小化 $f(x, y)$,应沿着负梯度方向 $-(2, 2)$ 移动,即更新参数: $$ x \leftarrow x - \alpha \cdot 2x $$ $$ y \leftarrow y - \alpha \cdot 2y $$ 其中 $\alpha$ 是学习率。 梯度在机器学习中的作用 在机器学习中,梯度表示损失函数(Loss Function)对模型参数的敏感度。例如,对于模型参数 $W$(权重)和 $b$(偏置),梯度 $\nabla L$ 包含两个分量: $$ \nabla L = \left( \frac{\partial L}{\partial W}, \frac{\partial L}{\partial b} \right) $$ 通过沿着负梯度方向更新参数(即梯度下降),可以逐步降低损失函数的值。 梯度下降的示例 目标:最小化函数 (线性回归的损失函数)。 $$ L(W, b) = (W \cdot x + b - y_{\text{true}})^2 $$ 假设 $$ x = 2, \quad y_{\text{true}} = 4, \quad W = 1, \quad b = 0.5 $$ 计算预测值: $$ y_{\text{pred}} = W \cdot x + b = 1 \cdot 2 + 0.5 = 2.5 $$ 计算损失: $$ L = (y_{\text{pred}} - y_{\text{true}})^2 = (2.5 - 4)^2 = 2.25 $$ 计算梯度: $$ \frac{\partial L}{\partial W} = 2 (y_{\text{pred}} - y_{\text{true}}) \cdot x = 2 (2.5 - 4) \cdot 2 = -6.0 $$ $$ \frac{\partial L}{\partial b} = 2 (y_{\text{pred}} - y_{\text{true}}) = 2 (2.5 - 4) = -3.0 $$ 梯度为 $$ \nabla L = (-6.0, -3.0) $$ 参数更新(学习率 $ (\alpha = 0.1))$: $$ W_{\text{new}} = W - \alpha \cdot \frac{\partial L}{\partial W} = 1 - 0.1 \cdot (-6.0) = 1.6 $$ $$ b_{\text{new}} = b - \alpha \cdot \frac{\partial L}{\partial b} = 0.5 - 0.1 \cdot (-3.0) = 0.8 $$ 梯度计算推导 这个公式是梯度计算中的一部分,计算的是损失函数 (L) 对参数 (W) 的偏导数。我们来一步步推导这个公式。 假设损失函数为: $$ L(W, b) = (W \cdot x + b - y_{\text{true}})^2 $$ 其中$ W$是权重,$b $是偏置,$x $是输入,$y_{\text{true}} $是真实的标签。我们要计算的是损失函数 $L$ 对权重 $W$的偏导数$ \frac{\partial L}{\partial W}$。 步骤 1: 定义损失函数 损失函数是预测值和真实值之间的误差的平方,定义为: $$ L(W, b) = (y_{\text{pred}} - y_{\text{true}})^2 $$ 其中,$y_{\text{pred}} = W \cdot x + b $是模型的预测值。这个损失函数是一个二次函数,目标是最小化它。 步骤 2: 使用链式法则求梯度 我们需要对损失函数 (L) 关于 (W) 求偏导数。首先可以应用链式法则: $$ \frac{\partial L}{\partial W} = \frac{\partial L}{\partial y_{\text{pred}}} \cdot \frac{\partial y_{\text{pred}}}{\partial W} $$ 步骤 3: 计算每一部分的偏导数 第一部分: 计算 $\frac{\partial L}{\partial y_{\text{pred}}}$。 由于损失函数是平方误差形式: $$ L = (y_{\text{pred}} - y_{\text{true}})^2 $$ 对$y_{\text{pred}}$求导,得到: $$ \frac{\partial L}{\partial y_{\text{pred}}} = 2(y_{\text{pred}} - y_{\text{true}}) $$ 第二部分: 计算 $\frac{\partial y_{\text{pred}}}{\partial W}$。 由于 $y_{\text{pred}} $= $W \cdot x + b$,对$ W $求导,得到: $$ \frac{\partial y_{\text{pred}}}{\partial W} = x $$ 步骤 4: 合并结果 现在将两部分结果结合起来: $$ \frac{\partial L}{\partial W} = 2(y_{\text{pred}} - y_{\text{true}}) \cdot x $$ 步骤 5: 将具体数值代入 根据给定的数值 $x = 2$,$ y_{\text{true}} = 4$, $W = 1$, 和 $b = 0.5$,我们首先计算预测值$y_{\text{pred}}$: $$ y_{\text{pred}} = W \cdot x + b = 1 \cdot 2 + 0.5 = 2.5 $$ 然后代入到梯度公式中: $$ \frac{\partial L}{\partial W} = 2(2.5 - 4) \cdot 2 = 2(-1.5) \cdot 2 = -6.0 $$ 所以,损失函数$ L$ 对 $W $的偏导数是 $-6.0$。 总结:对于复杂的梯度计算可以利用链式法则。在该示例中,先令 $y_{\text{pred}} $= $W \cdot x + b$。对$W$求偏导,就可以转化为,$\frac{\partial L}{\partial W} = \frac{\partial L}{\partial y_{\text{pred}}} \cdot \frac{\partial y_{\text{pred}}}{\partial W}$,然后可以先求$\frac{\partial L}{\partial y_{\text{pred}}} $,再求$\frac{\partial y_{\text{pred}}}{\partial W}$,这样计算就没有这么复杂了。根据公式$\frac{\partial L}{\partial y_{\text{pred}}} = 2(y_{\text{pred}} - y_{\text{true}})$,而$\frac{\partial y_{\text{pred}}}{\partial W} = x$,所以$\frac{\partial L}{\partial W} = 2(y_{\text{pred}} - y_{\text{true}}) \cdot x$,因此知道预测值、真实值、输入值、当前的权重和偏置即可算出偏导。同理$b$也可以用类似方法,继而算出损失函数的梯度$\nabla L = \left( \frac{\partial L}{\partial W}, \frac{\partial L}{\partial b} \right)$ pytorch示例 在pytorch中通过自动微分Autograd自动计算梯度,示例如下: import torch # 定义参数(启用梯度追踪) W = torch.tensor(1.0, requires_grad=True) b = torch.tensor(0.5, requires_grad=True) # 输入数据 x = torch.tensor(2.0) y_true = torch.tensor(4.0) # 前向传播 y_pred = W * x + b loss = (y_pred - y_true) ** 2 # 反向传播计算梯度 loss.backward() # 输出梯度 print(f"dL/dW: {W.grad}") # 输出 tensor(-6.0) print(f"dL/db: {b.grad}") # 输出 tensor(-3.0) 概念 数学表达 意义 梯度定义 ∇f = (∂f/∂x₁, …) 多元函数变化最快的方向及其速率 梯度下降 W ← W − α ⋅ ∂L/∂W 沿负梯度方向更新参数以最小化损失函数 PyTorch自动微分 loss.backward() 通过反向传播自动计算所有参数的梯度并存储在 .grad 中 -
激活函数
概念 前面我们主要使用的是线性模型,但是线性模型有很多局限性,因为我们要建模的问题并不能单纯使用线性模型就能够拟合的,如下示例。 我们要拟合红色部分的函数,使用线性模型即使在怎么调整W和b都没法进行拟合出来,要拟合这样的函数,我们需要非线性的函数。 如上图,要拟合这样的模型,我们可以使用①②③函数相加再加上一个b偏置。那这里的①②③函数怎么来了,可以看出是wx+b再经过一个sigmoid转换得来,那这里的sigmoid我们就称为激活函数。 激活函数的主要作用是引入非线性,使得神经网络能够处理更复杂的问题并避免退化成线性模型。没有激活函数,神经网络就无法发挥其强大的学习和表达能力。选择合适的激活函数对模型的训练和性能表现至关重要。 常见的激活函数 ReLU 激活函数 公式:$ \text{ReLU}(x) = \max(0, x) $ x = torch.arange(-8.0, 8.0, 0.1, requires_grad=True) y = torch.relu(x) d2l.plot(x.detach(), y.detach(), 'x', 'relu(x)', figsize=(5, 2.5)) ReLU激活函数用得比较多,因为其计算相对简单,不需要复杂的指数计算,因为指数计算都很贵。 ReLU函数进行求导,可以发现当输入为负时,导数为0,当输入为正是,导数为1。可以使用y.backward来计算导数,可以理解导数就是梯度。x取不同位置进行求导得到的值,就是相应位置的梯度。 y.backward(torch.ones_like(x), retain_graph=True) d2l.plot(x.detach(), x.grad, 'x', 'grad of relu', figsize=(5, 2.5)) Sigmoid 激活函数 公式: $ \sigma(x) = \frac{1}{1 + e^{-x}} $ y = torch.sigmoid(x) d2l.plot(x.detach(), y.detach(), 'x', 'sigmoid(x)', figsize=(5, 2.5)) Tanh 激活函数 公式: $ \tanh(x) = \frac{e^{x} - e^{-x}}{e^{x} + e^{-x}} $ y = torch.tanh(x) d2l.plot(x.detach(), y.detach(), 'x', 'tanh(x)', figsize=(5, 2.5)) Softmax 激活函数 公式: $ \text{Softmax}(z_i) = \frac{e^{z_i}}{\sum_{j=1}^{K} e^{z_j}} $ 在前面章节中,我们使用softmax用于线性回归的多分类,但其实softmax也可以看做一种激活函数。 softmax将神经网络的输出转换为概率分布,确保每个类别的输出值在0到1之间,且所有类别的概率和为1。如z=[2.0,1.0,0.1] 经过softmax计算转化后得[0.7,0.2,0.1],如果神经网络的输出为三个类别的得分,表示第一个类别的预测概率最大,约为70%。 总结来说,Softmax 是一种激活函数,它专门用于多分类问题的输出层,帮助模型生成一个概率分布,便于做分类决策。 -
sotfmax回归实现
什么是sotfmax回归 Softmax回归(Softmax Regression),也叫多项逻辑回归,是一种用于多分类问题的分类算法。它是对逻辑回归(Logistic Regression)的一种扩展,适用于处理输出类别数大于2的情况。Softmax回归通过使用Softmax函数来将每个类别的输出转化为一个概率分布,使得输出值能够表示每个类别的概率,并且所有类别的概率之和为1。 举个例子:假设有一个包含3个类别的多分类问题:苹果、香蕉、橙子。对于每个输入样本(例如一张图片),Softmax回归模型会输出三个值(每个类别的概率),也就是概率分布。例如: 苹果的概率:0.6 香蕉的概率:0.3 橙子的概率:0.1 这些概率加起来等于1,模型会将输入样本分类为苹果(因为概率最大)。 softmax函数 对于每个类别$ k $ ,我们会计算一个得分$ z_k $,然后将这个得分转化为概率。得分通常是由输入数据$ \mathbf{x} $与对应类别的权重向量$ \mathbf{w}_k $ 的线性组合给出的:$ z_k = \mathbf{w}_k^T \mathbf{x} + b_k $, 其中,$ \mathbf{w}_k $ 是第$ k$ 个类别的权重,$ b_k$ 是偏置项,$ \mathbf{x} $ 是输入特征向量。Softmax函数用于将这些得分$ z_k $转换成概率。 Softmax函数的形式如下:$ P(y = k | \mathbf{x}) = \frac{e^{z_k}}{\sum_{j=1}^K e^{z_j}} $ 。 $ P(y = k | \mathbf{x}) ) 是输入 ( \mathbf{x} $ 属于类别k的概率。 $ z_k $ 是类别 $ k $ 的得分。 $ \sum_{j=1}^K e^{z_j} $ 是所有类别得分的指数函数的和,确保概率和为1。 交叉熵损失函数 为了训练Softmax回归模型,我们使用交叉熵损失函数来评估模型预测与真实标签之间的差异。交叉熵损失函数的公式如下:$ L(\theta) = - \sum_{i=1}^N \sum_{k=1}^K y_{ik} \log P(y_k = 1 | \mathbf{x}_i) $ 其中: - $ N $ 是训练集中的样本数。 - $ y_{ik} $ 是样本 $ i $是否属于类别 $ k $ 的标签(通常是1或0)。 - $ P(y_k = 1 | \mathbf{x}_i) $ 是输入 $ \mathbf{x}_i $ 属于类别 $ k $ 的概率。 softmax实现示例 数据读取 pip install d2l==0.16 import torch from IPython import display from d2l import torch as d2l batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) 这里直接使用了d2l.load_data_fashion_mnist() 函数加载 Fashion-MNIST 数据集。load_data_fashion_mnist 是 d2l 库中的一个工具函数,用于加载 Fashion-MNIST 数据集并返回训练集和测试集的数据迭代器。train_iter 是训练集的迭代器。test_iter 是测试集的迭代器。数据迭代器是用于在模型训练和评估过程中批量加载数据的对象。batch_size 参数指定了每个批次包含多少个样本。 可以用下面的示例代码打印输入的数据 n = 10 for X, y in train_iter: break X_selected = X[0:n].reshape((n, 28, 28)) titles = [f'Label: {int(label)}' for label in y[0:n]] d2l.show_images(X_selected, 1, n, titles=titles) 定义模型 sotfmax函数 计算softmax的步骤如下: 对每个项求幂(使用exp) 对每一行求和(小批量中每个样本是一行),得到每个样本的规范化常数 将每一行除以其规范化常数,确保结果的和为1 def softmax(X): X_exp = torch.exp(X) print(X_exp) partition = X_exp.sum(1, keepdim=True) print(partition) return X_exp / partition 示例如下: X = torch.normal(0, 1, (2, 5)) #使用正态分布生成2行5列的矩阵 print(X) X_prob = softmax(X) X_prob, X_prob.sum(1) #生成的数据 tensor([[ 0.3141, 0.5186, -0.6949, 0.5918, -2.2370], [-0.3814, 0.8092, -0.1959, 0.7489, 1.8790]]) #torch.exp(X):对矩阵中每个数据求e^x指数运算后的结果 tensor([[1.3690, 1.6797, 0.4991, 1.8072, 0.1068], [0.6829, 2.2460, 0.8221, 2.1146, 6.5472]]) #X_exp.sum(1, keepdim=True): 对每一行求和 tensor([[ 5.4618], [12.4129]]) #将每一行除以其规范化常数,确保结果的和为1 (tensor([[0.2506, 0.3075, 0.0914, 0.3309, 0.0196], [0.0550, 0.1809, 0.0662, 0.1704, 0.5275]]), tensor([1., 1.])) 模型和参数 num_inputs = 784 num_outputs = 10 W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True) b = torch.zeros(num_outputs, requires_grad=True) def net(X): return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b) 模型还是使用的是线性模式,只是在线性模型的基础上再加了一个softmax函数。模型的数学表示为:$ \hat{y} = \text{softmax}(X W + b) $ $ X \in \mathbb{R}^{n \times 784} $ 是输入样本矩阵,$ n $ 是样本数量。 $ W \in \mathbb{R}^{784 \times 10} $ 是权重矩阵。 $ b \in \mathbb{R}^{10} $ 是偏置向量。 $ \hat{y} \in \mathbb{R}^{n \times 10} $ 是输出矩阵,其中每一行是一个样本的预测类别概率。 softmax 函数的公式为:$ \text{softmax}(z_i) = \frac{e^{z_i}}{\sum_{j} e^{z_j}} $ 其中 $ z_i $ 是某一类别的得分,$ j $ 遍历所有类别(在这个例子中是 10 个类别)。通过 softmax 函数,每个输出都会被转换为一个概率,所有类别的概率加起来为 1。 定义损失函数 def cross_entropy(y_hat, y): return - torch.log(y_hat[range(len(y_hat)), y]) y_hat:这是模型的预测输出,通常是一个经过 softmax 函数处理的概率分布。y_hat 的形状通常是 (batch_size, num_classes),其中 batch_size 是样本数量,num_classes 是类别数量。每一行表示一个样本对各个类别的预测概率。 y:这是实际标签的索引,形状为 (batch_size,),表示每个样本的真实类别的索引。 y_hat[range(len(y_hat)), y]:这是通过 y 中的类别索引提取 y_hat 中对应类别的预测概率。range(len(y_hat)) 生成一个从 0 到 batch_size-1 的索引序列,表示每个样本。通过 y 索引,获取每个样本对应类别的概率值。 torch.log(...):对提取的预测概率取对数。交叉熵损失函数中有一个 log 操作,它衡量了预测概率和真实标签之间的差异。 负号:交叉熵是通过负对数似然(negative log-likelihood)计算的,因此需要对结果取负。 损失函数的公式为:$ L = - \frac{1}{n} \sum_{i=1}^{n} \log(\hat{y}_{i, y_i}) $, 通过对每个样本的预测概率取对数,并对所有样本的对数损失求和再取负值。 分类精度 分类精度= 样本预测正确数量除以样本总数(len(y))。 也可以理解是预测对的概率,比如输入样本图片识别正确数为1,总样本数2时,精度为 1/2 = 0.5。 先看看例子,y_hat模型的预测输出,通常是一个二维矩阵,形状为 (样本数, 类别数)。例如,2个样本(输入的图片)3个类别(猫、狗、猪)的输出可能是 [[0.1, 0.2, 0.7], [0.3, 0.4, 0.3]],即每个样本对应输出的一个概率分布,样本1对应的概率分布[0.1, 0.2, 0.7],样本2对应的概率分布是[0.3, 0.4, 0.3],而真实的标签y是一个一维向量,每个元素表示对应样本的正确类别索引,如[2, 1],其中2代表的是狗,1代表猫。 那y_hat和y怎么做比较和转换了? 解决的办法就是,我们取每个样本概率分布中最大概率的索引,也就是通过 argmax(axis=1) 沿着行方向(即每个样本)找到概率最大的类别索引。例如,[[0.1, 0.2, 0.7], [0.3, 0.4, 0.3]] 会得到 [2, 1], 即第一行最大是0.7,索引位置是2,第二行最大是0.4,级索引是1。有了这样的结果,就可以y_hat和y做比较了,比如y=[2,1], 那么y_hat输出结果是[2,1],那么表示全部预测对。 def accuracy(y_hat, y): if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: y_hat = y_hat.argmax(axis=1) #y_hat将输出索引如[2,1],下面结算的是y_hat和y进行比较,返回正确的个数。 cmp = y_hat.type(y.dtype) == y return float(cmp.type(y.dtype).sum()) 因此上面这个函数,最终返回的是正确的个数,比如y_hat = [[0.1, 0.2, 0.7], [0.3, 0.4, 0.3]],y是[2,2]经过accuracy函数处理后,返回的是结果是1, 因为y_hat = y_hat.argmax(axis=1)计算后,返回的是[2,1],与实际的标签[2,2]有一个不对,即第二个样本预测错了。那么最终的分类精度就等于1/2 = 0.5。 训练 def train_epoch_ch3(net, train_iter, loss, updater): # 将模型设置为训练模式 if isinstance(net, torch.nn.Module): net.train() # 训练损失总和、训练准确度总和、样本数 metric = Accumulator(3) for X, y in train_iter: # 计算梯度并更新参数 y_hat = net(X) l = loss(y_hat, y) if isinstance(updater, torch.optim.Optimizer): # 使用PyTorch内置的优化器和损失函数 updater.zero_grad() l.mean().backward() updater.step() else: # 使用定制的优化器和损失函数 l.sum().backward() updater(X.shape[0]) metric.add(float(l.sum()), accuracy(y_hat, y), y.numel()) # 返回训练损失和训练精度 return metric[0] / metric[2], metric[1] / metric[2] def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9], legend=['train loss', 'train acc', 'test acc']) for epoch in range(num_epochs): train_metrics = train_epoch_ch3(net, train_iter, loss, updater) //返回训练损失和训练精度 test_acc = evaluate_accuracy(net, test_iter) //返回的是测试精度 animator.add(epoch + 1, train_metrics + (test_acc,)) //将其绘制到图像上。 train_loss, train_acc = train_metrics assert train_loss < 0.5, train_loss assert train_acc <= 1 and train_acc > 0.7, train_acc assert test_acc <= 1 and test_acc > 0.7, test_acc lr = 0.1 def updater(batch_size): return d2l.sgd([W, b], lr, batch_size) num_epochs = 10 train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater) 下面是训练的过程显示: train loss: 训练损失,也就是损失函数的结果。是模型在训练集上的平均损失值,通常使用损失函数来衡量。例如,常用的交叉熵损失(cross-entropy loss)或均方误差(mean squared error)。损失越小,说明模型在训练数据上的表现越好。它反映了模型预测值与真实标签之间的差距。 train acc: Training Accuracy, 训练精度。是模型在训练集上的正确预测的比例。它通过比较模型的预测结果和真实标签来计算。训练精度=正确预测的样本数量/总样本数量。训练准确度越高,说明模型在训练数据上的拟合程度越好。训练准确度反映了模型对训练集的学习能力。 test acc: Test Accuracy,测试精度。是指模型在未见过的测试集上的准确度。它与训练准确度不同,测试集用来评估模型的泛化能力。测试准确度反映了模型对新数据的预测能力。如果测试准确度高,说明模型不仅在训练集上表现好,而且具有较强的泛化能力,能够适应未见过的数据。 预测 def predict_ch3(net, test_iter, n=6): for X, y in test_iter: break trues = d2l.get_fashion_mnist_labels(y) preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1)) titles = [true +'\n' + pred for true, pred in zip(trues, preds)] d2l.show_images( X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n]) predict_ch3(net, test_iter) 使用训练好的模型,来预测实际的效果: 总结 一、公式和代码 公式:y = softmax(WX+b) 代码实现:y = softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b) 二、输入和输出示例 输入: X= torch.Size([256, 1, 28, 28])-->X=torch.Size([256, 784]) 由于WX要满足矩阵乘,所以要把X做处理X.reshape((-1, W.shape[0])) W=torch.Size([784, 10]) b=torch.Size([10]) 输出: y_hat=torch.Size([256, 10]) -->([256,784])*([784,10]) = ([256, 10])矩阵相乘 下面是打印第一行的结果,也就是对应输入第一个样本的预测结果。 最后为0.99613,如果最后一项是代表是shirt,但是表示第一个样本就是shirt。 tensor([4.8291e-06, 1.2489e-07, 3.7127e-06, 2.1127e-07, 1.3334e-06, 2.6440e-03, 1.9091e-05, 8.7211e-04, 3.2460e-04, 9.9613e-01]) 本文来自: <动手学深度学习 V2> 的学习笔记 -
线性回归实现
线性回归 线性回归模型根据给定的数据集和对应的标签,通过一个函数模型来拟合数据集以及对应标签的映射关系。而这个模型可以设置为y=wx+b的一个函数,其中x和w是一个向量。目标就是找出权重w和偏执b的值,使得模型更逼近数据集合的规律,也就是能够预测的更准确。 线性回归示例实现 pytorch本身有线性回归的函数,只是这里通过实现pytoch来加深理解 读取数据集 def data_iter(batch_size, features, labels): num_examples = len(features) #获取数据的长度,假1000行,输出1000 indices = list(range(num_examples)) #生成一个下标,结果[0,...,999] random.shuffle(indices)#打散indices,使数据随机,结果[77,99,0,13,....] for i in range(0, num_examples, batch_size): #表示从0到num_examples,步长为 batch_size batch_indices = torch.tensor( indices[i: min(i + batch_size, num_examples)]) print(batch_indices) #i 到 i + batch_size 的索引转换为一 PyTorch张量 yield features[batch_indices], labels[batch_indices] #每次循环时,yield 会返回一个元组 (features_batch, labels_batch), #其中 features_batch 是一个包含该批次特征数据的 Tensor,labels_batch #是该批次对应的标签数据。 定义一个函数data_iter,将数据集(x)、以及数据集对应的特征(y)作为函数输入,分割成大小为batch_size的小批量数据集(x)和特征集(y)。之所以要进行分割每次抽取小批量样本,是利用了GPU并行运算的优势。每个样本都可以并行地进行模型计算,同时在后续计算梯度时,每个样本损失函数的梯度可以被并行计算。 batch_size = 10 for X, y in data_iter(batch_size, features, labels): print(X, '\n', y) break 运行结果 tensor([940, 41, 385, 262, 655, 402, 317, 256, 984, 644]) --print(batch_indices) tensor([[-0.9666, 0.8299], [-1.8890, 0.1645], [ 0.0274, -0.6944], [ 2.0289, 0.7227], [ 1.0077, 0.6674], [ 1.8692, 0.5002], [-0.9469, 1.7404], [ 0.8589, -0.5467], [ 1.1260, 0.1262], [-0.6988, -0.0683]]) tensor([[-0.5347], [-0.1296], [ 6.6105], [ 5.7961], [ 3.9675], [ 6.2448], [-3.5983], [ 7.7625], [ 6.0183], [ 3.0294]]) 那么训练的数据集和特征怎么来了,一般是通过需要训练的目标处理得来,为了方便本章用一个函数来模拟生成数据集。 def synthetic_data(w, b, num_examples): X = torch.normal(0, 1, (num_examples, len(w))) y = torch.matmul(X, w) + b y += torch.normal(0, 0.01, y.shape) return X, y.reshape((-1, 1)) 上面这个函数,实际上就是给先指了w和b生成y=wx+b模型中的x和y, 而我们就是要训练找出w和b。 生成输入数据 X:torch.normal(mean, std, size) 用于从正态分布中生成数据。这里,mean=0 表示均值为0,std=1 表示标准差为1。(num_examples, len(w)) 是生成张量的形状,这里 num_examples 是生成的样本数,len(w) 是每个样本的特征数(即权重向量 w 的长度)。所以 X 是一个形状为 (num_examples, len(w)) 的矩阵,其中包含了从标准正态分布中采样的特征数据。 生成标签 y: torch.matmul(X, w) 计算输入特征 X 和权重 w 的矩阵乘法。结果是一个形状为 (num_examples,) 的张量,表示每个样本的预测值(不包括偏置)。+ b 将偏置 b 加到每个样本的预测值中,这样就得到最终的标签 y。这就是线性回归模型中的公式 y = Xw + b。 添加噪声: torch.normal(0, 0.01, y.shape) 生成一个与 y 形状相同的噪声项,噪声来自均值为 0,标准差为 0.01 的正态分布。这一步是为了给数据添加一些随机噪声,使得生成的数据更符合实际情况。现实中,数据通常会有一些误差或噪声,因此我们在标签 y 上添加小的随机波动。 返回数据: X 是生成的输入特征数据。y.reshape((-1, 1)) 将标签 y 转换为一个形状为 (num_examples, 1) 的列向量,以确保标签的形状是列向量。 生成合成的线性数据集,数据集的特征 X 是从标准正态分布中采样的,而标签 y 是通过线性方程 y = Xw + b 生成的,并且在 y 上添加了一些小的噪声。 true_w = torch.tensor([2, -3.4]) true_b = 4.2 features, labels = synthetic_data(true_w, true_b, 10) print('features:', features, '\nfeatures len', len(features), '\nlabel:', labels) 运行结果 features: tensor([[ 4.3255e-01, -1.4288e+00], [ 2.2412e-01, -1.8749e-01], [-5.6843e-01, 1.0930e+00], [ 1.3660e+00, -1.8141e-03], [ 3.9331e-01, -2.4553e-02], [-6.3184e-01, -8.4748e-01], [-1.7891e-02, -1.4018e+00], [-4.8070e-01, 8.5689e-01], [ 2.0670e+00, 3.8301e-02], [ 1.7682e+00, 1.9595e-01]]) features len 10 label: tensor([[ 9.9307], [ 5.2856], [-0.6669], [ 6.9439], [ 5.0759], [ 5.8344], [ 8.9642], [ 0.3175], [ 8.2140], [ 7.0458]]) 定义模型 我们的模型函数是y=wX+b,也就是计算输入特征X和权重W,这里的Xw是一个向量,而b是一个标量,但是用一个向量加上一个标量是,标量会被加到每个分量上,这是广播机制。 def linreg(X, w, b): return torch.matmul(X, w) + b 在开始计算随机梯度下降优化模型参数之前,需要先预设一些参数。下面是使用正态分布随机初始化w和b。 w = torch.normal(0, 0.01, size=(2,1), requires_grad=True) b = torch.zeros(1, requires_grad=True) w, b 定义损失函数 损失函数就是根据我们采样处理的数据X输入到我们的模型中计算处理的值y’跟真实值y的差距,这里使用平方损失函数,即loss=(y’-y)^2,详细的公式为:$ L(w, b) = \frac{1}{n} \sum_{i=1}^{n} \ell^{(i)}(w, b) = \frac{1}{n} \sum_{i=1}^{n} \left( \frac{1}{2} \left( w^\top x^{(i)} + b - y^{(i)} \right)^2 \right) $ def squared_loss(y_hat, y): return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2 优化参数 详细的公式为:$ (w, b) \leftarrow (w, b) - \frac{\eta}{|B|} \sum_{i \in B} \nabla_{(w, b)} \ell^{(i)}(w, b) $ 损失函数是对对应参数求的偏导,即如果是$w$就是对$w$的偏导,如果是$b$就是$b$的偏导,$x$是当前采样的具体值(不是变量),公式中的$B$是抽样的小批量,是固定数量的训练样本。 具体算法的步骤如下,对于$W$更新参数的公式为: $ w \leftarrow w - \frac{\eta}{|B|} \sum_{i \in B} \frac{\partial \ell^{(i)}}{\partial w}(w, b) = w - \frac{\eta}{|B|} \sum_{i \in B} x^{(i)} \left( w^\top x^{(i)} + b - y^{(i)} \right) $ 对于$b$更新参数的公式为: $ b \leftarrow b - \frac{\eta}{|B|} \sum_{i \in B} \frac{\partial \ell^{(i)}}{\partial b}(w, b) = b - \frac{\eta}{|B|} \sum_{i \in B} \left( w^\top x^{(i)} + b - y^{(i)} \right) $ 从上面的公式可以看出,梯度是批量误差的和,没处理一个批量数据,更新一次参数,而不是每处理一个数据更新一次参数。 def sgd(params, lr, batch_size): with torch.no_grad(): for param in params: param -= lr * param.grad / batch_size #梯度值为param.grad param.grad.zero_() param.grad是哪里来的? 系统自动计算而来,下一章节会介绍。 训练 在训练之前,需要先初始化参数, w = torch.normal(0, 0.01, size=(2,1), requires_grad=True) b = torch.zeros(1, requires_grad=True) w, b 接下来开始训练 lr = 0.03 num_epochs = 5000 net = linreg loss = squared_loss for epoch in range(num_epochs): for X, y in data_iter(batch_size, features, labels): l = loss(net(X, w, b), y) # X和y的小批量损失 # 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起, # 并以此计算关于[w,b]的梯度 l.sum().backward() sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数 print('true_w',true_w, 'w', w, '\ntrue_b', true_b, 'b',b) with torch.no_grad(): train_l = loss(net(features, w, b), labels) print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}') print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}') print(f'b的估计误差: {true_b - b}') 为什么l.sum().backward能够自动计算存储梯度值? 在初始化w和b的参数时,设定了requires_grad=True。在计算损失时,net(X, w, b)会生成预测值y_hat,并通过loss函数与真实值y构建计算图。调用l.sum().backward()时,PyTorch的autograd系统会从标量损失l.sum()开反向传播,自动计算w和b的梯度,并存储在w.grad和b.grad中。 tensor([4, 3, 9, 6, 8, 2, 5, 7, 1, 0]) ---batch_size = 10 true_w tensor([ 2.0000, -3.4000]) w tensor([[ 2.0056],[-3.4014]], requires_grad=True) true_b 4.2 b tensor([4.1983], requires_grad=True) epoch 1, loss 0.000056 tensor([5, 6, 2, 4, 0, 7, 8, 1, 9, 3]) true_w tensor([ 2.0000, -3.4000]) w tensor([[ 2.0056],[-3.4014]], requires_grad=True) true_b 4.2 b tensor([4.1983], requires_grad=True) epoch 2, loss 0.000056 tensor([8, 5, 6, 9, 7, 4, 2, 1, 0, 3]) true_w tensor([ 2.0000, -3.4000]) w tensor([[ 2.0056],[-3.4014]], requires_grad=True) true_b 4.2 b tensor([4.1983], requires_grad=True) epoch 3, loss 0.000056 tensor([9, 3, 5, 2, 8, 0, 7, 4, 6, 1]) true_w tensor([ 2.0000, -3.4000]) w tensor([[ 2.0056],[-3.4014]], requires_grad=True) true_b 4.2 b tensor([4.1983], requires_grad=True) epoch 4, loss 0.000056 tensor([8, 1, 5, 3, 0, 6, 2, 4, 9, 7]) true_w tensor([ 2.0000, -3.4000]) w tensor([[ 2.0056],[-3.4014]], requires_grad=True) true_b 4.2 b tensor([4.1983], requires_grad=True) epoch 5, loss 0.000056 tensor([3, 7, 4, 0, 6, 9, 2, 1, 5, 8]) true_w tensor([ 2.0000, -3.4000]) w tensor([[ 2.0056],[-3.4014]], requires_grad=True) true_b 4.2 b tensor([4.1983], requires_grad=True) epoch 6, loss 0.000056 tensor([7, 4, 6, 1, 0, 5, 3, 8, 9, 2]) true_w tensor([ 2.0000, -3.4000]) w tensor([[ 2.0056],[-3.4014]], requires_grad=True) true_b 4.2 b tensor([4.1983], requires_grad=True) 误差结果: w的估计误差: tensor([-0.0056, 0.0014], grad_fn=<SubBackward0>) b的估计误差: tensor([0.0017], grad_fn=<RsubBackward1>) 本文来自: <动手学深度学习 V2> 的学习笔记 -
perf工具使用
perf介绍 perf 是一个强大的 Linux 性能分析工具,广泛用于分析程序的性能瓶颈,帮助开发者进行调优。perf 工具能够收集并分析多种硬件和软件事件,包括 CPU 的指令执行、缓存命中与失误、上下文切换等。 硬件事件驱:通过访问 CPU 的 PMU(性能监控单元)捕获硬件级事件,如 CPU 周期数、缓存命中/未命中、分支预测失败等。 采样与统计机制:采样模式,周期性记录程序执行状态,生成热点函数分布(默认基于 CPU 时钟周期);统计模式,精确记录特定事件的发生次数(如指令数、缓存访问次数); 内核集成优势:直接调用内核的 tracepoint 和 kprobe 机制,支持用户态与内核态的全栈追 基本语法: perf <command> [options] command:perf 工具的子命令,例如 record、stat、report 等。 options:提供给 command 的选项和参数。 perf record perf record 命令用于收集性能数据,通常用来分析程序的性能瓶颈。 perf record [options] <command> -p \<pid>:指定要分析的进程的 PID。 -F \<frequency>:指定采样的频率(每秒钟采样次数)。例如,-F 99 每秒采样 99 次。 -g:收集调用图信息(调用栈信息),可以用来分析函数调用的上下文。 -e \<event>:指定要计数的事件。例如:-e cycles 计数 CPU 周期,-e cache-misses 计数缓存未命中 -- sleep \<time>:执行指定命令,并在给定的时间内采样性能数据。例如,-- sleep 30 表示记录 30 秒的数据。 示例: perf record -F 99 -p 12345 -g -- sleep 30 # 这会对进程 PID 为 12345 的程序进行 30 秒的性能采样,采样频率为99Hz(默认是1000HZ),并收集调用图信息。 # record生成的是原始数据bin,无法直接查看,默认是生成perf.data,可以使用-o指定输出文件。 # 需要使用perf script转化才可解析。 perf report perf report 命令用于分析和展示 perf record 记录的性能数据。 perf report [options] -g:显示调用图(调用堆栈)信息,帮助分析函数的调用关系,如果要绘制图像,需要加这个参数。 -i \<file>:指定输入文件,默认情况下会使用 perf.data 文件。 示例: perf report -g 这会显示 perf record 记录的性能数据的调用图。 perf script perf script 是一个 perf 工具的子命令,主要用于将 perf record 采集到的性能数据转换为可读的格式,并允许用户对其进行进一步处理。它的主要功能是解析性能数据文件并输出到标准输出或指定文件,方便进一步分析。 perf script [options] 将 perf record 生成的性能数据(默认文件名为 perf.data)转化为易于阅读的文本格式。可以与其他工具结合,进一步分析和处理数据。 perf script -i perf.data > output.txt 指定输入文件转化为输出文件。 实践应用 perf sched perf sched 是 perf 工具中的一个子命令,用于分析与调度相关的性能数据,主要用于分析 Linux 系统中的调度器行为(即进程和线程的调度)。这个命令可以帮助开发人员深入了解进程或线程如何在 CPU 上执行,以及在多核系统上如何分配 CPU 时间。 perf sched 命令通过分析内核的调度事件(如进程切换、上下文切换、进程调度延迟等),帮助开发人员识别系统中可能的调度瓶颈或性能问题。 抓取数据 perf sched record -a -g -o sched_raw.data & 解析数据: killall perf #结束进程,注意是不要使用-9强行退出,需要等待退出,保证写入的文件完整。 perf sched timehist -i sched_raw.data > sched_timehist.log perf sched latency -i sched_raw.data > sched_latency.log # 显示进程或线程的调度延迟,帮助你理解调度延迟如何影响系统性能。 perf sched script -i sched_raw.data > sched.log 解析数据 killall perf #结束进程,注意是不要使用-9强行退出,需要等待退出,保证写入的文件完整。 perf sched timehist -i sched_raw.data > sched_timehist.log perf sched latency -i sched_raw.data > sched_latency.log # 显示进程或线程的调度延迟,帮助你理解调度延迟如何影响系统性能。 perf sched script -i sched_raw.data > sched.log perf irq perf irq是perf 工具中的一个子命令,用于分析与中断(IRQ, Interrupt Request)相关的性能数据。中断是操作系统用来响应硬件或软件事件的机制。perf irq 可以帮助开发者分析中断的发生频率、持续时间及其对系统性能的影响。 抓取数据 抓取中断的进入和退出 perf record -e irq:irq_handler_entry,irq:irq_handler_exit -a -g -o irq_raw.data & 解析数据 killall perf perf script -i irq_raw.data > irq.log perf report -i irq_raw.data > irq_report.log 火焰图 通过perf script将原始数据转换的数据,可以使用工具转换为火焰图。需要注意的时,在使用perf script转换之前,perf record需要加-g参数,记录调用栈。 火焰图工具下载链接:https://github.com/brendangregg/FlameGraph 下面是转换命令: ../FlameGraph-master/stackcollapse-perf.pl < sched.log | ../FlameGraph-master/flamegraph.pl > sched.svg 其中sched.log是perf script转换的处理的数据,先使用stackcollapse-perf.pl处理数据,然后再使用flamegraph.pl绘制图像,即可使用网页打开。 y轴(竖)表示调用栈,每一层都是一个函数,调用栈越深,火焰就越高,顶部就是正在执行的函数,下方都是它的父函数。 x轴(横)表示抽样数,若一个函数在x轴占据的宽度越宽,就表示它被抽到的次数多,即执行的时间长。注意,x 轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的。 火焰图就是看顶层的哪个函数占据的宽度最大。只要有"平顶"(plateaus),就表示该函数可能存在性能问题。 点击一层会水平放大,左上角会同时显示"Reset Zoom",点击该链接,图片就会恢复原样。 -
非暴力沟通
非暴力沟通要素 观察: 不带评论的观察 感受:体会(对方)和表达(自己)感受(区分想法和感受) 需要:体会(对方)和表达(自己)需求 请求:体会(对方)和表达(自己)请求(一定是要具体的请求,不要含糊) 沟通的过程 观察实际发生了什么事实? 表达出我们看到的这些行为的感受 表达出我们的感受与什么需求相关联 说出我们一个具体的请求 疏离生命的语言 道德评判: 别人的行为与我们价值观不符,便认为这个人是错的。如指责、辱骂说别人蠢、混蛋、冷漠等等色彩的词。 做比较:与比人做比较,谁谁怎么样?你怎么样?或者自己怎么样? 不得不: 我们不是主人,是被强迫的要求的 推卸责任:将行动归结于外部因素,如我必须得怎么做?谁安排的?不得不怎么做? 其他:“要求”的方式 不带评论的观察(事实) 不夹杂任何评论,而是将看到的事实。区分出观察与评论,不夹杂任何。 评论:他冲我发脾气,他是个好人,他太懒了。 观察:他用拳头砸了一下桌子,他将他收入1/10捐给了慈善机构,他一天到晚都躺在床上玩手机。 当别人说你不好时?有以下做法 法1:讨好他,承认自己的不足。 法2:反击他,这是他的不对。 法3:不辩解也不指责,试着沟通找出对方的观察 作者给出的结论见P36。 体会和表达感受(是什么) 先来区分一下感受和想法? 感受:是指我们在某个情境中体验到的情绪或身体感受,比如快乐、愤怒、悲伤、焦虑、兴奋等。这些情绪是我们内心的真实反应,与外部事件的关系直接,但它们是我们对外界刺激的个人体验。 想法: 是我们对某种情境的理解、分析、判断或信念,它们通常是我们大脑的认知活动,如“我觉得他不尊重我”或“我认为这不公平”。想法往往是我们对自己感受的解释或推理。 区分方法: 感受通常是情绪或身体上的反应,是一种“感觉”或“状态”,比如“我感到害怕”或“我感到开心”。想法是对事件的解释或评判,通常以“我认为”或“我觉得”开始,比如“我认为他没有听我说话”或“我觉得这件事不对”。 举例: 感受:我感到伤心和失望。 想法:我觉得他没有关注我的感受。 总结,简单来说,感受是情绪和身体状态,想法是思维和判断。 为什么要尽可能表达感受而不是想法 表达感受反应的是我们内心的真实体验,没有带评判,而是呈现我们的情绪状态。而表达想法往往包含对对方的评价或指责,容易激发对方的防御反应。表达感受专注我们内心的体验,容易获得理解或共鸣。 怎么表达自己的感受? 表达感受时,尽可能要清晰,核心点事尽量的先不要说评价、评论、判断,容易导致对方防御。 表达感受的词:开心、平静、放松、担心、难过、尴尬、不舒服。 表达想法的词:这样做不对,你让我失望,你对不起我,得不到支持,拒绝。 体会和表达需要(为什么) 感受的根因(为什么) 需求则是指我们内心深处的基本需求,能够驱动我们行为的动机。它是抽象的、长期性的,并且是感受的根本原因。例如,当我们感到沮丧时,可能是因为我们的需求(如尊重、理解、归属感等)没有得到满足。 对他人的指责、批评、评论以及分析反应了我们的需要和价值观,如果我们通过批评来提出主张,人们的反应常常是申辩或反击。反之如果我们直接输出我们的需要,其他人就较有可能做出积极回应。 大多数人倾向考虑别人有什么错,而不是习惯从别人需要什么来考虑。因此在于别人沟通时,关注别人的感受,然后从感受中体会需求。可以经过沟通进一步确认。 请求帮助(怎么做) 请求(How),请求需要具体 -
站点centos系统迁移ubuntu系统
概述 本站点最早使用的是centos系统,但是该系统官方不再更新维护了,随最近将服务器的系统更换为ubuntu系统,先记录站点的迁移过程。 迁移要点 由于本站点主要是基于php+nginx+wordpress框架设计。备份的原理是php、nginx、wordpress软件属于工具是可以在新的系统上新安装,但是wordpress相关的配置如主题、数据库、插件等属于数据是需要进行复制迁移的,因此备份的关键核心就是打包wordpress+mysql数据库。 wordpress:wordpress组件包括插件、主题等。 数据库:wordpress所需要的数据库。 上面两个主要就是站点的关键数据了,需要原封迁移到新的服务器上去。其他次要点依情况选择: nginx配置:一般只需要把ssl的证书保留就行,不过也可以在云平台上重新下载。保守就是把/etc/nginx都打包一份。 版本:注意记录一下php、nginx的版本,以免出现版本不匹配。 迁移方式 迁移的方式有两种方式,一种是使用wordpress插件的方式,另外就是完全手动复制的方式。下面先总结一下这两种方式关键的路径: 使用wordpress插件方式 该方式主要使用wordpress插件WPvivid Backup插件,该插件可以直接备份wordpress+数据库。核心思路是: 备份wordpress+数据库:在WPvivid Backup后台做一次最新的备份。 备份nginx(可选):/etc/nginx压缩打包,可以只保留证书也行。 存储:将备份文件存储到本地或云端。 迁移:在服务器重置安装新的系统,然后将备份文件复制到新的服务器上。 安装:搭建一个新的php、nginx、wordpress环境。包括配置nginx和创建wordpress新的数据库链接等。 恢复:在新的wordpress站点上安装WPviviBackup插件,扫描备份文件进行还原。 配置:重新配置nginx支持https等。 使用手动方式 该方式不使用插件,手动进行会稍微多一些步骤。核心思路如下: 备份wordpress:将wordpress进行压缩打包。 备份数据库:使用mysqldump备份数据库。 备份nginx(可选):/etc/nginx压缩打包,可以只保留证书也行。 存储:将备份的文件存储到本地或云端。 迁移:服务器重置系统后,将备份文件复制到新服务器上。 按装:按照php、nginx。 恢复:将wordpress解压到nginx的主页目录下,恢复mysql数据库。 配置:重新配置nginx支持https等。 插件方式 备份 步骤1:备份wordpress与数据库 在插件WPvivid Backup后台备份wordpress+数据库到本地,然后通过ftp或scp拷贝到本地电脑。 步骤2:备份nginx的证书 在服务器/etc/nginx目录下,把cert证书压缩同理通过ftp或scp拷贝到本地电脑。这个证书路径可能不一样,根据自己实际情况。 重装 用插件做好数据迁移到本地备份后,即可销毁服务器上的系统重新安装了,怎么安装选择系统这里就不阐述了,本文重装的是ubuntu系统,系统启动后进行如下步骤按照软件。 步骤1:按照基本的软件 首先,确保软件包列表是最新的,这样可以避免安装过时的软件版本。 sudo apt update 如果你想升级已安装的软件到最新版本,使用:sudo apt upgrade 安装 Nginx:sudo apt install nginx 检查 Nginx 服务状态:sudo systemctl status nginx 启动Nginx:sudo systemctl start nginx 安装 MySQL:sudo apt install mysql-server 启动MySQL: sudo systemctl start mysql 安装 PHP 和相关的扩展:sudo apt install php-fpm php-mysql php-cli php-curl php-mbstring php-xml php-zip php-gd 安装其他如curl 或 git:sudo apt install curl git 按照wordpress: wget https://wordpress.org/latest.tar.gz tar -xzvf latest.tar.gz mv wordpress /var/www/html/ 步骤2:配置wordpress的数据库 先创建一个数据库。 mysql -u root -p CREATE DATABASE wordpress_db; CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'your_password'; GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wpuser'@'localhost'; FLUSH PRIVILEGES; EXIT; 创建一个mysql的数据库,用于给wordpress创建一个数据库。 wordpress_db:数据库的名称。 wpuser: 访问数据库的用户名。 your_password:访问数据需要的密码。 接着修改wordpress的配置可以访问数据库。 cd /var/www/html/wordpress/ cp wp-config-sample.php wp-config.php vim wp-config.php 更新刚设置的数据库名 define('DB_NAME', 'wordpress_db'); define('DB_USER', 'wpuser'); define('DB_PASSWORD', 'your_password'); define( 'DB_HOST', 'localhost' ); 步骤3:配置nginx 先做个备份 cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default_backup vim /etc/nginx/sites-available/default server { listen 80; server_name laumy.tech www.laumy.tech; root /var/www/html/wordpress; index index.php index.html index.htm index.nginx-debian.html; location / { try_files $uri $uri/ /index.php?$args; } location ~ .php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.3-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } location ~ /.ht { deny all; } } server { listen 443 ssl; server_name laumy.tech www.laumy.tech; ssl_certificate /etc/nginx/cert/www.laumy.tech.pem; ssl_certificate_key /etc/nginx/cert/www.laumy.tech.key; root /var/www/html/wordpress; index index.php index.html index.htm index.nginx-debian.html; location / { try_files $uri $uri/ /index.php?$args; } location ~ .php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.3-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } location ~ /.ht { deny all; } } 一个是配置http的监听端口,另外一个是配置https的监听端口。 server_name: 表示监听的网站,填自己的域名。 root /var/www/html/wordpress: 指定了网站寻址的路径,这里是wordpress存放的路径。 ssl_certificate**: 这个指定的是https的证书路径,只有443这个端口才需要填。 配置好后执行: sudo nginx -t && sudo systemctl reload nginx 修改wordpress配置强制支持https,vim /var/www/html/wordpress/wp-config.php,加上下面两行。 define('FORCE_SSL_ADMIN', true); define('FORCE_SSL_LOGIN', true); 步骤4:登录配置wordpress 没什么问题的话,登录自己的域名就可以进入到wordpress界面设置用户名和密码进入到后台了。 注意记得把后台的站点地址用https的方式,不然http和https混合访问的问题。 恢复 按照前面的步骤,顺利进入到wordpress后台之后,到这里就简单了。只需要按照插件WPvivid Backup进行还原即可。 步骤1:将备份文件拷贝到插件指定目录 默认一般是在/var/www/html/wordpress/wp-content/wpvividbackups 步骤:还原 在插件界面扫描到备份文件,进行还原。等还原完成,到此就大功告成了。 手动方式 手动跟插件的方式这里只列出差异点,就不再过多赘述了。 备份 对wordpress整个文件夹进行压缩,其次对mysql数据库进行备份。 mysqldump -u root -p your_database_name > wordpress-db-backup.sql your_database_name 是你的数据库名称,要是忘记了就去wordpress/wp-config.php看看DB_NAME,密码亦是如此如下。 define('DB_NAME', 'wordpress_db'); define('DB_USER', 'wpuser'); define('DB_PASSWORD', 'your_password'); 接着把wordpress压缩包、sql、nginx的证书拷贝到本地或云端。 恢复 将wordpress解压到/var/www/html/下。然后对数据库进行恢复,这里重点补充一下。 首先是要在mysql里面创建一个新的数据库。 mysql -u root -p CREATE DATABASE wordpress_db; CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'your_password'; GRANT ALL PRIVILEGES ON wordpress_db.* TO 'laumy'@'localhost'; FLUSH PRIVILEGES; exit 注意这里的wordpress_db、wpuser、your_password分别为数据库名称、用户名、密码。可以跟你wordpress里面wp-config.php的一样,这样到时候就不用改了。 接入导入新的数据库。 mysql -uwpuser -pyour_password wordpress_db < wordpress-db-backup.sql 这样数据库就导入完成了。 总结一下,无论是那种方式的备份,最关键的就是将wordpress和数据库能够迁移复制过去就行。而php、nginx正常按照新的就行。而nginx网站的配置,主要就是注意将wordpress要解压位置设置好,以及读写权限要配置好。其次就是配置/etc/nginx/sites-available/default指定好网站的root路径(即wordpress路径)和证书的路径即可完成。 最后要是要是过程中遇到问题可以询问ai,当下ai已经非常强大了,只要把环境信息告知好,基本上所有的问题都能能解决。 -
站点开源
Laumy Fresh Theme 本站点是基于wordpress设计,现开源其主题代码,让感兴趣的爱好者直接可复用。源码地址:https://github.com/laumy0929/wd-laumy-fresh-theme。 🚀 快速开始 方法一:WordPress后台安装(推荐) 下载主题压缩包 laumy-fresh-theme.zip 在WordPress后台进入 外观 > 主题 点击 添加新主题 > 上传主题 选择下载的压缩包文件 点击 现在安装 并激活主题 方法二:FTP上传 解压主题文件到本地 通过FTP上传到 /wp-content/themes/laumy-fresh-theme/ 目录 在WordPress后台激活主题 方法三:Git克隆 cd wp-content/themes/ git clone https://github.com/laumy0929/wd-laumy-fresh-theme.git 🌟 主题特色 设计理念 简洁美观:采用现代简约设计风格,专注于内容阅读体验 响应式布局:完美适配桌面端、平板和移动设备 深色模式:支持一键切换深色/浅色主题 高效性能:优化的CSS和JavaScript,确保快速加载 核心功能 📱 三栏布局:左侧分类导航 + 中间内容区域 + 右侧个人信息 🔍 智能搜索:顶部居中搜索框,快速查找内容 📂 分类管理:支持多级分类展开/收起,显示文章数量 📖 文章目录:自动生成多级目录,支持滚动高亮 💬 评论系统:完整的WordPress评论功能 📊 数据统计:显示文章数量和阅读量统计 🎨 主题切换:一键切换深色/浅色模式 ⚙️ 主题配置详解 个人资料设置 进入自定义设置 在WordPress后台点击 外观 > 自定义 或者点击 外观 > 主题 中的 自定义 按钮 配置个人信息 在 作者信息 部分设置: 头像: 点击 选择图片 上传个人头像 推荐尺寸:320×180 像素 支持格式:JPG、PNG、GIF 如果不上传,会显示默认头像 昵称: 输入你想显示的名称 会显示在右侧个人信息卡片的顶部 支持中文和英文 职业: 输入你的职业或身份标签 会显示为昵称右上角的绿色徽章 例如:前端工程师、全栈开发、技术博主 个性签名: 输入个人简介或座右铭 会显示在昵称下方 支持多行文本 保存设置 点击 发布 按钮保存所有设置 分类菜单配置 创建主菜单 进入 外观 > 菜单 点击 创建新菜单 输入菜单名称(如:主菜单) 选择菜单位置为 Primary Menu 添加分类到菜单 在左侧 分类 面板中选择需要的分类 点击 添加到菜单 拖拽调整分类顺序 设置父子分类关系: 将子分类拖拽到父分类下方 向右缩进表示层级关系 菜单结构示例 📁 技术笔记 📁 前端开发 📁 HTML/CSS 📁 JavaScript 📁 Vue.js 📁 后端开发 📁 PHP 📁 Python 📁 Node.js 📁 运维部署 📁 Linux 📁 Docker 📁 Nginx 文章缩略图设置 方法一:设置特色图片(推荐) 在编辑文章时,找到右侧 特色图片 面板 点击 设置特色图片 上传或选择图片 推荐尺寸:160×100 像素 点击 设置特色图片 确认 方法二:在文章内容中插入图片 在文章开头位置插入图片 主题会自动提取第一张图片作为缩略图 建议图片尺寸:160×100 像素或更大 缩略图优先级 特色图片(最高优先级) 文章内容第一张图片 默认占位图(如果以上都没有) 默认缩略图位置 文件路径:assets/images/default-thumbnail.svg 可以替换为自定义图片 建议保持 160×100 像素尺寸 文章目录功能 自动生成规则 基于文章中的 H2-H5 标题自动生成 支持无限级嵌套目录 自动编号:1、1.1、1.1.1... 使用方法 在文章中使用标准的标题标签: <h2>主要章节</h2> <h3>子章节</h3> <h4>详细内容</h4> 目录会自动显示在文章左侧 支持点击跳转和滚动高亮 目录样式 左侧:📋 剪贴板图标 右侧:☰ 汉堡菜单图标 支持展开/收起功能 深色模式配置 启用深色模式 在页面顶部找到主题切换按钮(太阳/月亮图标) 点击即可切换深色/浅色模式 设置会自动保存到浏览器本地存储 深色模式特性 自动调整所有颜色 背景色:深灰色 (#181a1b) 文字色:浅色 (#eceff1) 卡片背景:深色 (#202225) 📱 响应式设计说明 桌面端 (>1200px) 完整三栏布局 左侧分类导航:260px 右侧个人信息:300px 中间内容区域:自适应宽度 平板端 (992px-1200px) 保持三栏布局 侧边栏宽度自动调整 内容区域适当缩小 移动端 (<768px) 单列布局 隐藏网站标题和部分导航 搜索框居中显示 保留主题切换功能 🎨 自定义样式 CSS变量 主题使用CSS变量,便于自定义: :root { --header-height: 56px; --sidebar-left-width: 260px; --sidebar-right-width: 300px; --container-max-width: 1800px; --gap: 14px; --color-bg: #ffffff; --color-text: #222; --color-muted: #666; --color-primary: #3498db; --color-border: #e6e6e6; } 自定义颜色 在 外观 > 自定义 > 颜色 中调整: - 主色调 - 背景色 - 文字颜色 - 边框颜色 📁 文件结构 laumy-fresh-theme/ ├── style.css # 主题样式文件 ├── index.php # 首页模板 ├── single.php # 文章页面模板 ├── category.php # 分类页面模板 ├── search.php # 搜索结果模板 ├── page.php # 页面模板 ├── header.php # 头部模板 ├── footer.php # 底部模板 ├── functions.php # 主题功能文件 ├── comments.php # 评论模板 ├── searchform.php # 搜索表单 ├── screenshot.png # 主题截图 ├── assets/ │ ├── js/ │ │ └── theme.js # 主题JavaScript │ └── images/ │ ├── default-thumbnail.svg # 默认缩略图 │ └── default-profile.svg # 默认头像 └── README.md # 说明文档 🔧 高级功能配置 评论系统设置 进入 设置 > 讨论 启用 嵌套评论 功能 设置评论审核规则 配置评论通知 搜索功能优化 进入 设置 > 固定链接 选择 文章名 或 自定义结构 保存设置以优化搜索 性能优化 启用WordPress缓存插件 压缩图片文件 定期清理数据库 使用CDN加速 🎯 使用技巧 分类管理最佳实践 使用清晰的分类名称 合理设置分类层级(建议不超过3级) 定期整理和合并相似分类 为每个分类添加描述 文章写作建议 使用标准的H2-H5标题结构 在文章开头插入相关图片 设置合适的特色图片 添加适当的标签 用户体验优化 定期更新个人资料 保持分类结构清晰 使用高质量的缩略图 及时回复评论 🐛 常见问题解答 Q: 分类不显示或显示异常? A: 检查以下设置: 1. 在 外观 > 菜单 中是否设置了主菜单 2. 菜单位置是否选择为 Primary Menu 3. 分类是否已添加到菜单中 4. 分类是否有文章内容 Q: 个人资料头像不显示? A: 检查以下设置: 1. 在 外观 > 自定义 > 作者信息 中是否上传了头像 2. 图片格式是否支持(JPG、PNG、GIF) 3. 图片大小是否合适(建议320×180像素) Q: 文章缩略图显示异常? A: 尝试以下方法: 1. 设置文章特色图片 2. 在文章开头插入图片 3. 检查图片格式和大小 4. 替换默认缩略图文件 Q: 深色模式切换不生效? A: 检查以下设置: 1. 确保浏览器支持localStorage 2. 清除浏览器缓存 3. 检查是否有JavaScript错误 4. 尝试刷新页面 Q: 移动端显示异常? A: 检查以下设置: 1. 主题已针对移动端优化 2. 检查是否有其他插件冲突 3. 清除浏览器缓存 4. 检查CSS是否被其他插件覆盖 Q: 文章目录不显示? A: 检查以下设置: 1. 文章内容是否包含H2-H5标题 2. 标题标签是否正确使用 3. 检查JavaScript是否正常加载 4. 查看浏览器控制台是否有错误 📞 交流方式 作者:Laumy 邮箱:laumy0929@gmail.com 网站:http://www.laumy.tech/ GitHub:https://github.com/laumy0929/wd-laumy-fresh-theme 📄 许可证 本主题基于 GPL v2 或更高版本开源协议发布。