第一章:PyTorch 入门
1.1 Pytorch 简介
1.1.1 PyTorch的由来
很多人都会拿PyTorch和Google的Tensorflow进行比较,这个肯定是没有问题的,因为他们是最火的两个深度学习框架了。但是说到PyTorch,其实应该先说Torch。
1.1.2 Torch是什么?
Torch英译中:火炬
A Tensor library like Numpy, unlike Numpy it has strong GPU support. Lua is a wrapper for Torch (Yes! you need to have a good understanding of Lua), and for that you will need LuaRocks package manager.
Torch是一个与Numpy类似的张量(Tensor)操作库,与Numpy不同的是Torch对GPU支持的很好,Lua是Torch的上层包装。
Torch is not going anywhere. PyTorch and Torch use the same C libraries that contain all the performance: TH, THC, THNN, THCUNN and they will continue to be shared.
We still and will have continued engineering on Torch itself, and we have no immediate plan to remove that.
PyTorch和Torch使用包含所有相同性能的C库:TH, THC, THNN, THCUNN,并且它们将继续共享这些库。
这样的回答就很明确了,其实PyTorch和Torch都使用的是相同的底层,只是使用了不同的上层包装语言。
注:LUA虽然快,但是太小众了,所以才会有PyTorch的出现。
1.1.3 重新介绍 PyTorch
PyTorch is an open source machine learning library for Python, based on Torch, used for applications such as natural language processing. It is primarily developed by Facebook's artificial-intelligence research group, and Uber's "Pyro" software for probabilistic programming is built on it.
PyTorch是一个基于Torch的Python开源机器学习库,用于自然语言处理等应用程序。 它主要由Facebook的人工智能研究小组开发。Uber的"Pyro"也是使用的这个库。
PyTorch is a Python package that provides two high-level features:
Tensor computation (like NumPy) with strong GPU acceleration
Deep neural networks built on a tape-based autograd system
You can reuse your favorite Python packages such as NumPy, SciPy and Cython to extend PyTorch when needed.
PyTorch是一个Python包,提供两个高级功能:
- 具有强大的GPU加速的张量计算(如NumPy)
- 包含自动求导系统的的深度神经网络
1.1.4 对比PyTorch和Tensorflow
没有好的框架,只有合适的框架, 这篇知乎文章有个简单的对比,所以这里就不详细再说了。
并且技术是发展的,知乎上的对比也不是绝对的,比如Tensorflow在1.5版的时候就引入了Eager Execution机制实现了动态图,PyTorch的可视化,windows支持,沿维翻转张量等问题都已经不是问题了。
1.1.5 再次总结
- PyTorch算是相当简洁优雅且高效快速的框架
- 设计追求最少的封装,尽量避免重复造轮子
- 算是所有的框架中面向对象设计的最优雅的一个,设计最符合人们的思维,它让用户尽可能地专注于实现自己的想法
- 大佬支持,与google的Tensorflow类似,FAIR的支持足以确保PyTorch获得持续的开发更新
- 不错的的文档(相比FB的其他项目,PyTorch的文档简直算是完善了,参考Thrift),PyTorch作者亲自维护的论坛 供用户交流和求教问题
- 入门简单
所以如果以上信息有吸引你的内容,那么请一定要读完这本书:)
1.2 Pytorch环境搭建
PyTorch的安装十分简单,根据PyTorch官网,对系统选择和安装方式等灵活选择即可。
这里以anaconda为例,简单的说一下步骤和要点。
国内安装anaconda建议使用清华镜像。
前些日子,由于合规问题中科大、清华镜像都已经关闭。目前只有清华镜像恢复,所以目前可以继续使用
1.2.1 安装Pytorch
pytorch的安装经过了几次变化,请大家以官网的安装命令为准。另外需要说明的就是在1.2版本以后,pytorch只支持cuda 9.2以上了,所以需要对cuda进行升级,目前测试大部分显卡都可以用,包括笔记本的MX250也是可以顺利升级到cuda 10.1。
我个人测试使用官网的安装命令进行安装时并不能安装1.3版原因未知(如果大家conda安装也有问题可以一起讨论下原因),所以这里建议大家使用pip进行安装,经过测试 pip是没有任何问题的。
目前(2020/7)的稳定版本为1.5.1。
#默认 使用 cuda10.2
pip3 install torch===1.5.1 torchvision===0.6.1 -f https://download.pytorch.org/whl/torch_stable.html
#cuda 9.2
pip3 install torch==1.5.1+cu92 torchvision==0.6.1+cu92 -f https://download.pytorch.org/whl/torch_stable.html
#cpu版本
pip install torch==1.5.1+cpu torchvision==0.6.1+cpu -f https://download.pytorch.org/whl/torch_stable.html
验证输入python 进入
import torch
torch.__version__
# 得到结果'1.5.0'
1.2.2 配置 Jupyter Notebook
新建的环境是没有安装安装ipykernel的所以无法注册到Jupyter Notebook中,所以先要准备下环境
#安装ipykernel
conda install ipykernel
#写入环境
python -m ipykernel install --name pytorch --display-name "Pytorch for Deeplearning"
下一步就是定制 Jupyter Notebook
#切换回基础环境
activate base
#创建jupyter notebook配置文件
jupyter notebook --generate-config
## 这里会显示创建jupyter_notebook_config.py的具体位置
打开文件,修改
c.NotebookApp.notebook_dir = '' 默认目录位置
c.NotebookApp.iopub_data_rate_limit = 100000000 这个改大一些否则有可能报错
1.2.3 测试
至此 Pytorch 的开发环境安装完成,可以在开始菜单中打开Jupyter Notebook 在New 菜单中创建文件时选择Pytorch for Deeplearning 创建PyTorch的相关开发环境了
1.2.4 问题解决
问题1:启动python提示编码错误
删除 .python_history 来源
问题2 默认目录设置不起效
打开快捷方式,看看快捷方式是否跟这个截图一样,如果是则删除 %USERPROFILE% 改参数会覆盖掉notebook_dir设置,导致配置不起效

如果你还发现其他问题,请直接留言
1.3 PyTorch 深度学习:60分钟快速入门 (官方)
%matplotlib inline
1. 张量
PyTorch是什么?
基于Python的科学计算包,服务于以下两种场景:
- 作为NumPy的替代品,可以使用GPU的强大计算能力
- 提供最大的灵活性和高速的深度学习研究平台
开始
Tensors(张量)
Tensors与Numpy中的 ndarrays类似,但是在PyTorch中
Tensors 可以使用GPU进行计算.
from __future__ import print_function
import torch
创建一个 5x3 矩阵, 但是未初始化:
x = torch.empty(5, 3)
print(x)
tensor([[1.0745e-38, 1.1112e-38, 1.0286e-38],
[1.0653e-38, 1.0194e-38, 8.4490e-39],
[1.1112e-38, 9.5511e-39, 1.0102e-38],
[4.6837e-39, 5.0510e-39, 5.1429e-39],
[9.9184e-39, 9.0000e-39, 1.0561e-38]])
创建一个随机初始化的矩阵:
x = torch.rand(5, 3)
print(x)
tensor([[0.6972, 0.0231, 0.3087],
[0.2083, 0.6141, 0.6896],
[0.7228, 0.9715, 0.5304],
[0.7727, 0.1621, 0.9777],
[0.6526, 0.6170, 0.2605]])
创建一个0填充的矩阵,数据类型为long:
x = torch.zeros(5, 3, dtype=torch.long)
print(x)
tensor([[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
创建tensor并使用现有数据初始化:
x = torch.tensor([5.5, 3])
print(x)
tensor([5.5000, 3.0000])
根据现有的张量创建张量。 这些方法将重用输入张量的属性,例如, dtype,除非设置新的值进行覆盖
x = x.new_ones(5, 3, dtype=torch.double) # new_* 方法来创建对象
print(x)
x = torch.randn_like(x, dtype=torch.float) # 覆盖 dtype!
print(x) # 对象的size 是相同的,只是值和类型发生了变化
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
tensor([[ 0.5691, -2.0126, -0.4064],
[-0.0863, 0.4692, -1.1209],
[-1.1177, -0.5764, -0.5363],
[-0.4390, 0.6688, 0.0889],
[ 1.3334, -1.1600, 1.8457]])
获取 size
译者注:使用size方法与Numpy的shape属性返回的相同,张量也支持shape属性,后面会详细介绍
print(x.size())
torch.Size([5, 3])
Note
``torch.Size`` 返回值是 tuple类型, 所以它支持tuple类型的所有操作.
操作
操作有多种语法。
我们将看一下加法运算。
加法1:
y = torch.rand(5, 3)
print(x + y)
tensor([[ 0.7808, -1.4388, 0.3151],
[-0.0076, 1.0716, -0.8465],
[-0.8175, 0.3625, -0.2005],
[ 0.2435, 0.8512, 0.7142],
[ 1.4737, -0.8545, 2.4833]])
加法2
print(torch.add(x, y))
tensor([[ 0.7808, -1.4388, 0.3151],
[-0.0076, 1.0716, -0.8465],
[-0.8175, 0.3625, -0.2005],
[ 0.2435, 0.8512, 0.7142],
[ 1.4737, -0.8545, 2.4833]])
提供输出tensor作为参数
result = torch.empty(5, 3)
torch.add(x, y, out=result)
print(result)
tensor([[ 0.7808, -1.4388, 0.3151],
[-0.0076, 1.0716, -0.8465],
[-0.8175, 0.3625, -0.2005],
[ 0.2435, 0.8512, 0.7142],
[ 1.4737, -0.8545, 2.4833]])
替换
# adds x to y
y.add_(x)
print(y)
tensor([[ 0.7808, -1.4388, 0.3151],
[-0.0076, 1.0716, -0.8465],
[-0.8175, 0.3625, -0.2005],
[ 0.2435, 0.8512, 0.7142],
[ 1.4737, -0.8545, 2.4833]])
Note
任何 以``_`` 结尾的操作都会用结果替换原变量. 例如: ``x.copy_(y)``, ``x.t_()``, 都会改变 ``x``.
你可以使用与NumPy索引方式相同的操作来进行对张量的操作
print(x[:, 1])
tensor([-2.0126, 0.4692, -0.5764, 0.6688, -1.1600])
torch.view: 可以改变张量的维度和大小
译者注:torch.view 与Numpy的reshape类似
x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8) # size -1 从其他维度推断
print(x.size(), y.size(), z.size())
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
如果你有只有一个元素的张量,使用.item()来得到Python数据类型的数值
x = torch.randn(1)
print(x)
print(x.item())
tensor([-0.2368])
-0.23680149018764496
Read later:
100+ Tensor operations, including transposing, indexing, slicing,
mathematical operations, linear algebra, random numbers, etc.,
are described
here <https://pytorch.org/docs/torch>_.
NumPy 转换
将一个Torch Tensor转换为NumPy数组是一件轻松的事,反之亦然。
Torch Tensor与NumPy数组共享底层内存地址,修改一个会导致另一个的变化。
将一个Torch Tensor转换为NumPy数组
a = torch.ones(5)
print(a)
tensor([1., 1., 1., 1., 1.])
b = a.numpy()
print(b)
[1. 1. 1. 1. 1.]
观察numpy数组的值是如何改变的。
a.add_(1)
print(a)
print(b)
tensor([2., 2., 2., 2., 2.])
[2. 2. 2. 2. 2.]
NumPy Array 转化成 Torch Tensor
使用from_numpy自动转化
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a)
print(b)
[2. 2. 2. 2. 2.]
tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
所有的 Tensor 类型默认都是基于CPU, CharTensor 类型不支持到
NumPy 的转换.
CUDA 张量
使用.to 方法 可以将Tensor移动到任何设备中
# is_available 函数判断是否有cuda可以使用
# ``torch.device``将张量移动到指定的设备中
if torch.cuda.is_available():
device = torch.device("cuda") # a CUDA 设备对象
y = torch.ones_like(x, device=device) # 直接从GPU创建张量
x = x.to(device) # 或者直接使用``.to("cuda")``将张量移动到cuda中
z = x + y
print(z)
print(z.to("cpu", torch.double)) # ``.to`` 也会对变量的类型做更改
tensor([0.7632], device='cuda:0')
tensor([0.7632], dtype=torch.float64)
2. Autograd: 自动求导
本章是冲突的重灾区,建议阅读
%matplotlib inline
Autograd: 自动求导机制
PyTorch 中所有神经网络的核心是 autograd 包。
我们先简单介绍一下这个包,然后训练第一个简单的神经网络。
autograd包为张量上的所有操作提供了自动求导。
它是一个在运行时定义的框架,这意味着反向传播是根据你的代码来确定如何运行,并且每次迭代可以是不同的。
示例
张量(Tensor)
torch.Tensor是这个包的核心类。如果设置
.requires_grad 为 True,那么将会追踪所有对于该张量的操作。
当完成计算后通过调用 .backward(),自动计算所有的梯度,
这个张量的所有梯度将会自动积累到 .grad 属性。
要阻止张量跟踪历史记录,可以调用.detach()方法将其与计算历史记录分离,并禁止跟踪它将来的计算记录。
为了防止跟踪历史记录(和使用内存),可以将代码块包装在with torch.no_grad():中。
在评估模型时特别有用,因为模型可能具有requires_grad = True的可训练参数,但是我们不需要梯度计算。
在自动梯度计算中还有另外一个重要的类Function.
Tensor and Function are interconnected and build up an acyclic
graph, that encodes a complete history of computation. Each tensor has
a .grad_fn attribute that references a Function that has created
the Tensor (except for Tensors created by the user - their
grad_fn is None).
Tensor 和 Function互相连接并生成一个非循环图,它表示和存储了完整的计算历史。
每个张量都有一个.grad_fn属性,这个属性引用了一个创建了Tensor的Function(除非这个张量是用户手动创建的,即,这个张量的
grad_fn 是 None)。
如果需要计算导数,你可以在Tensor上调用.backward()。
如果Tensor是一个标量(即它包含一个元素数据)则不需要为backward()指定任何参数,
但是如果它有更多的元素,你需要指定一个gradient 参数来匹配张量的形状。
译者注:在其他的文章中你可能会看到说将Tensor包裹到Variable中提供自动梯度计算,Variable 这个在0.41版中已经被标注为过期了,现在可以直接使用Tensor,官方文档在这里:
https://pytorch.org/docs/stable/autograd.html#variable-deprecated
具体的后面会有详细说明
import torch
创建一个张量并设置 requires_grad=True 用来追踪他的计算历史
x = torch.ones(2, 2, requires_grad=True)
print(x)
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
对张量进行操作:
y = x + 2
print(y)
tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward0>)
结果y已经被计算出来了,所以,grad_fn已经被自动生成了。
print(y.grad_fn)
<AddBackward0 object at 0x000002004F7CC248>
对y进行一个操作
z = y * y * 3
out = z.mean()
print(z, out)
tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>)
.requires_grad_( ... ) 可以改变现有张量的 requires_grad属性。
如果没有指定的话,默认输入的flag是 False。
a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)
False
True
<SumBackward0 object at 0x000002004F7D5608>
梯度
反向传播
因为 out是一个纯量(scalar),out.backward() 等于out.backward(torch.tensor(1))。
out.backward()
print gradients d(out)/dx
print(x.grad)
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
得到矩阵 4.5.将 out叫做
Tensor “
o
o
o”.
得到
o
=
1
4
∑
i
z
i
o = \frac{1}{4}\sum_i z_i
o=41∑izi,
z
i
=
3
(
x
i
+
2
)
2
z_i = 3(x_i+2)^2
zi=3(xi+2)2 和
z
i
∣
x
i
=
1
=
27
z_i\bigr\rvert_{x_i=1} = 27
zi∣∣xi=1=27.
因此,
∂
o
∂
x
i
=
3
2
(
x
i
+
2
)
\frac{\partial o}{\partial x_i} = \frac{3}{2}(x_i+2)
∂xi∂o=23(xi+2), 则
∂
o
∂
x
i
∣
x
i
=
1
=
9
2
=
4.5
\frac{\partial o}{\partial x_i}\bigr\rvert_{x_i=1} = \frac{9}{2} = 4.5
∂xi∂o∣∣xi=1=29=4.5.
在数学上,如果我们有向量值函数
y
⃗
=
f
(
x
⃗
)
)
\vec{y} = f(\vec{x}))
y
=f(x
)) ,且
y
⃗
\vec{y}
y
关于
x
⃗
\vec{x}
x
的梯度是一个雅可比矩阵(Jacobian matrix):
J
=
(
∂
y
1
∂
x
1
⋯
∂
y
1
∂
x
n
⋮
⋱
⋮
∂
y
m
∂
x
1
⋯
∂
y
m
∂
x
n
)
J = \begin{pmatrix} \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{1}}{\partial x_{n}} \\ \vdots & \ddots & \vdots \\ \frac{\partial y_{m}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}} \end{pmatrix}
J=⎝⎜⎛∂x1∂y1⋮∂x1∂ym⋯⋱⋯∂xn∂y1⋮∂xn∂ym⎠⎟⎞
一般来说,torch.autograd就是用来计算vector-Jacobian product的工具。也就是说,给定任一向量
v
=
(
v
1
v
2
⋯
v
m
)
T
v=(v_{1}\;v_{2}\;\cdots\;v_{m})^{T}
v=(v1v2⋯vm)T ,计算
v
T
⋅
J
v^{T}\cdot J
vT⋅J ,如果
v
v
v 恰好是标量函数
l
=
g
(
y
⃗
)
l=g(\vec{y})
l=g(y
) 的梯度,也就是说
v
=
(
∂
l
∂
y
1
⋯
∂
l
∂
y
m
)
T
v=(\frac{\partial l}{\partial y_{1}}\;\cdots\;\frac{\partial l}{\partial y_{m}})^{T}
v=(∂y1∂l⋯∂ym∂l)T,那么根据链式法则,vector-Jacobian product 是
x
⃗
\vec{x}
x
对
l
l
l 的梯度:
J
T
⋅
v
=
(
∂
y
1
∂
x
1
⋯
∂
y
m
∂
x
1
⋮
⋱
⋮
∂
y
1
∂
x
n
⋯
∂
y
m
∂
x
n
)
(
∂
l
∂
y
1
⋮
∂
l
∂
y
m
)
=
(
∂
l
∂
x
1
⋮
∂
l
∂
x
n
)
J^{T}\cdot v = \begin{pmatrix} \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{1}} \\ \vdots & \ddots & \vdots \\ \frac{\partial y_{1}}{\partial x_{n}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}} \end{pmatrix} \begin{pmatrix} \frac{\partial l}{\partial y_{1}}\\ \vdots \\ \frac{\partial l}{\partial y_{m}} \end{pmatrix} = \begin{pmatrix} \frac{\partial l}{\partial x_{1}}\\ \vdots \\ \frac{\partial l}{\partial x_{n}} \end{pmatrix}
JT⋅v=⎝⎜⎛∂x1∂y1⋮∂xn∂y1⋯⋱⋯∂x1∂ym⋮∂xn∂ym⎠⎟⎞⎝⎜⎛∂y1∂l⋮∂ym∂l⎠⎟⎞=⎝⎜⎛∂x1∂l⋮∂xn∂l⎠⎟⎞
(注意,
v
T
⋅
J
v^{T}\cdot J
vT⋅J 给出了一个行向量,可以通过
J
T
⋅
v
J^{T}\cdot v
JT⋅v 将其视为列向量)
vector-Jacobian product 这种特性使得将外部梯度返回到具有非标量输出的模型变得非常方便。
现在让我们来看一个vector-Jacobian product的例子
x = torch.randn(3, requires_grad=True)
y = x * 2
while y.data.norm() < 1000:
y = y * 2
print(y)
tensor([ 293.4463, 50.6356, 1031.2501], grad_fn=<MulBackward0>)
在这个情形中,y不再是个标量。torch.autograd无法直接计算出完整的雅可比行列,但是如果我们只想要vector-Jacobian product,只需将向量作为参数传入backward:
gradients = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(gradients)
print(x.grad)
tensor([5.1200e+01, 5.1200e+02, 5.1200e-02])
如果.requires_grad=True但是你又不希望进行autograd的计算,
那么可以将变量包裹在 with torch.no_grad()中:
print(x.requires_grad)
print((x ** 2).requires_grad)
with torch.no_grad():
print((x ** 2).requires_grad)
True
True
False
稍后阅读:
autograd 和 Function 的官方文档 https://pytorch.org/docs/autograd
第一章:PyTorch 入门的更多相关文章
- Kettle解决方案: 第一章ETL入门
第一章ETL入门 1.1 OLPT和数据仓库对比 普通的事务系统和商业智能系统(BI)有什么区别? 1个独立的普通事务系统也被称为在线事务处理系统(OLTP) 商业智能系统也常被称为决策支持系统(DS ...
- MySQL----MySQL数据库入门----第一章 数据库入门
第一章 数据库入门 1.1 数据库基础知识 1.1.1 数据库概述 数据不仅包括普通意义上的数字,还包括文字.图像.声音等.也就是说,凡是在计算机中用来描述事物的记录都可称作数据. 数据库的基本特点: ...
- Java Persistence with MyBatis 3(中文版) 第一章 MyBatis入门
本章将涵盖以下话题: ž MyBatis是什么? ž 为什么选择MyBatis? ž MyBatis安装配置 ž 域模型样例 1.1 MyBatis是什么 MyBatis是一个简化和实现了Ja ...
- 第一章 Kubernetes入门
第一章 Kubernetes入门 kubernetes是基于容器技术的分布式架构领先方案,是一个完备的分布式系统支撑平台. kubernetes带来的好处:1)全面拥抱微服务:2)统可以随时随地整体“ ...
- Knockout应用开发指南 第一章:入门
2011-11-21 14:20 by 汤姆大叔, 20165 阅读, 17 评论, 收藏, 编辑 1 Knockout简介 (Introduction) Knockout是一个轻量级的UI类 ...
- 《C# to IL》第一章 IL入门
我们用C#.VB.NET语言编写的代码最终都会被编译成程序集或IL.因此用VB.NET编写的代码可以在C#中修改,随后在COBOL中使用.因此,理解IL是非常有必要的. 一旦熟悉了IL,理解.NET技 ...
- 烟大 Contest1024 - 《挑战编程》第一章:入门 Problem G: Check The Check(模拟国际象棋)
Problem G: Check The Check Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 10 Solved: 3[Submit][Statu ...
- 烟大 Contest1024 - 《挑战编程》第一章:入门 Problem D: LC-Display(模拟计算器显示数字)
Problem D: LC-Display Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 14 Solved: 3[Submit][Status][We ...
- Python之旅_第一章Python入门
一.编程语言分类 1.机器语言:即计算机能听懂的二进制语言,0000 0001,直接操控硬件: 2.汇编语言:简写的英文标识符代替二进制语言,本质同样是直接操控硬件: 3.高级语言:用更贴近人类的语言 ...
- Android初级教程理论知识(第一章快速入门)
一.综合介绍. Android项目的目录结构 Activity:应用被打开时显示的界面 src:项目代码 R.java:项目中所有资源文件的资源id Android.jar:Android的jar包, ...
随机推荐
- Servlet的学习之路
一.什么是什么Servlet? Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程 ...
- unidbgrid显示图片
column设置imageoptions属性,visible=true,设置width
- 不使用setTimeout的延迟执行
function sleep(ms){ var time = new Date(); time.setTime(time.getTime() + ms); while(new Date().getTi ...
- 记一个快捷在线接口YAPI
在线地址:http://192.168.252.152:3000 1.idea中file下setting中plugins搜索并加载插件YAPI 2.idea中的.idea中.misc.xml文件配置. ...
- NOIP2021 T1 报数 题解
AFO了,来最后写一波题解. T1 不算阴间,题意很直白,所以想先顺着题意打一波模拟. 算一下,发现 1e7 的 $O(nlog(n))$ 时间复杂度好像可以直接过( 实际上是$O(nloglog(n ...
- 问道Golang,6月龄必知必会(二)
在我看来,golnag有许多反直观的设计,而且这些设计通常不能自圆其说,导致gohper一而再再而三的调入陷阱. 网上也有很多gohper总结了一些笔记,我再提炼精简一下,挂在脑图树上便于记忆. 值类 ...
- 记一次 .NET某汽车零件采集系统 卡死分析
一:背景 1. 讲故事 前段时间有位朋友在微信上找到我,说他的程序会出现一些偶发卡死的情况,让我帮忙看下是怎么回事,刚好朋友也抓到了dump,就让朋友把 dump 丢给我,接下来用 windbg 探究 ...
- Echarts中slider滑块调整样式
今天遇到了一个问题,记录一下. 效果图. 原型图 一个页面中,引入了echarts的柱状图来动态显示数据,由于柱状图太高,echarts没有自动生成的滚动条,所以就用slider滑块手写了一个,但是效 ...
- 一个.Net Core开源缓存中间件,让你更加简单、方便使用缓存
上次给大家推荐过一个缓存中间件<一个C#开发的非常实用的缓存中间件>,今天再给大家推荐一个缓存中间件,两者功能差不多,都是提供统一接口.多级缓存.分布式缓存.支持多种Provider等. ...
- 了解CSS Module作用域隔离原理
CSS Module出现的背景 我们知道,Javascript发展到现在出现了众多模块化规范,比如AMD.CMD. Common JS.ESModule等,这些模块化规范能够让我们的JS实现作用域隔离 ...