梯度下降优化方法概述

Updated on 2023-06-24: add AdamW 梯度下降是优化神经网络和机器机器学习算法的首选优化方法。本文重度参考SEBASTIAN RUDER的文章。对于英文比较好的同学请直接阅读原文。本文只为个人的学习总结,难免有所欠缺和不足。 一、梯度下降变种 根据训练数据集的大小,梯度下降有三种变体,但是本质是一样的,不一样的是每次使用多少条样本。如果内存一次可以计算所有样本的梯度,称为:批梯度下降(Batch gradient descent);如果内存一次只允许一个样本,称为:随机梯度下降(Stochastic gradient descent);大部分时候,内存一次是可以计算部分样本的,称为:最小批梯度下降(Mini-batch gradient descent)。三种变体的数据表达如下: 1.1批梯度下降(Vanilla gradient descent,又称Batch gradient descent) $\theta = \theta - \eta \cdot \nabla_\theta J( \theta)$ 1.2随机梯度下降(Stochastic gradient descent) $\theta = \theta - \eta \cdot \nabla_\theta J( \theta; x^{(i)}; y^{(i)})$ 1.3最小批梯度下降(Mini-batch gradient descent) $\theta = \theta - \eta \cdot \nabla_\theta J( \theta; x^{(i:i+n)}; y^{(i:i+n)})$ 注意,在其他地方并没对上述三种变体做严格区别,统称为SGD(随机梯度下降),下文其余部分,我们也不加区分,统称为SGD 二、梯度下降的几种优化方法 传统的梯度下降法不能保证一个很好的收敛,而且有一些挑战需要被解决。 选择这个合适的学习率是比较困难的。特别是对一个新的模型和新数据集时候,我们是不知道选择什么样的学习率是合适的。只能不断的去尝试。 学习率调度算法可以在训练的过程中去调整模型的学习率。模型一开始的时候可以使用大一点的学习率,后面再使用小一点的学习率去微调模型。更好的方法是一开始也用一个小的学习率去warm-up训练,让参数先适应数据集。但是无论哪种学习率调度算法都需要预先定义调度算法,这种方法也是没有办法很好的适应模型的特征的、 对每一个参数都使用同样的学习率是不合适的。对于稀疏的数据或者特征非常不均衡的数据。最好是使用不同学习率学习不同频率的特征。 另外的挑战是对于高阶非凸的损失函数,往往会陷于局部极值点。还有一种鞍点的情况,模型也是很难学习的。此时损失函数在各个方向的梯度接近于0。SGD是很难逃脱与鞍点或者局部极值点的。 针对上面的一些问题,慢慢出现了一些针对梯度下降的优化方法。 在介绍SGD变种之前。先给出各个变种的一般范式。后天的各个变种优化方法都离不开这个范式。 (1)计算目标函数关于参数的梯度 $g_t = \nabla_\theta J( \theta)$...

十一月 28, 2021 · 1 分钟 · Pan

pytorch ddp

(1) 关键概念 world_size: 集群中所有GPU的数量 rank: 范围[0, world_size-1], 表示GPU的编号 local_rank: GPU在每台机器上的编号 比如,两台机器,每台机器4块卡,那么world_size= 2*4, rank 取值范围 [0,1,2,3,4,5,6,7], local_rank 取值范围[0,1,2,3] (2) torch.distributed.launch 启动集群参数 –nnodes: 一共多少台机器 –node_rank: 当前机器编号 –nproc_per_node: 每台机器多少个进程 –master_adderss: master节点ip地址 –master_port: master节点端口 master节点的node_rank必须为0 command example: python -m torch.distributed.launch --nnodes=2 --node_rank=0 --nproc_per_node 8 \ --master_adderss $my_address --master_port $my_port main.py (3) mp.spwan 启动 PyTorch引入了torch.multiprocessing.spawn,可以使得单卡、DDP下的外部调用一致,即不用使用torch.distributed.launch。 python xxxx.py一句话搞定DDP模式。 def demo_fn(rank, world_size): dist.init_process_group("nccl", rank=rank, world_size=world_size) # lots of code. ... def run_demo(demo_fn, world_size): mp.spawn(demo_fn, args=(world_size,), nprocs=world_size, join=True) (4) 集群训练步骤 import torch....

九月 15, 2021 · 2 分钟 · Pan

CompleableFuture原理和源码分析

CompleableFuture 使用场景 CompletableFuture的定义如下: public class CompletableFuture<T> implements Future<T>, CompletionStage<T> 我们看到CompletableFuture是实现了Future的接口的,在没有CompletableFuture之前,我们可以用FutureTask来实现一个Future的功能。那么有了FutureTask那么为什么还要有CompletableFuture呢? 我任务主要是CompletableFuture有两个优点 CompletableFuture可以实现完全的异步,而FutureTask必须通过get阻塞的方式获取结果 CompletableFuture .supplyAsync(()-> 1+2) .thenAccept((v)-> System.out.println(v*v)); 如上面的代码所示,我们完整的任务有两个阶段,一阶段是计算1+2,二阶段是计算一阶段返回结果的平方,在整个过程中,主线程完全不需要管这个任务的执行情况,也不会阻塞主线程。但是如果用FutureTask实现如上功能如下: FutureTask<Integer> futureTask1 = new FutureTask<Integer>(() -> { return 1 + 2; }); new Thread(futureTask1).start(); Integer periodOneResult = futureTask1.get(); FutureTask<Integer> futureTask2 = new FutureTask<Integer>(() -> { return periodOneResult * periodOneResult; }); new Thread(futureTask2).start(); Integer secondOneResult = futureTask2.get(); System.out.println(secondOneResult); 代码冗长不说,还需要get方法阻塞主线程去获取结果。以上代码只是说明CompletableFuture的异步优点,实际工作中你可以把两个任务看出两个api CompletableFuture可以实现复杂的任务编排,请思考下面代码的执行顺序是什么? CompletableFuture<String> base = new CompletableFuture<>(); CompletableFuture<String> completion0 = base.thenApply(s -> { System.out.println("completion 0"); return s + " 0"; }); CompletableFuture<String> completion1 = base....

4 分钟 · pan

docker命令

基础命令 查看有那些镜像 xxxxx:5000/v2/_catalog 查看具体项目的tag列表 xxxx:5000/v2/project/repo/tags/list 启动一个镜像 docker run -it --rm -v $PWD:/tmp -w /tmp self_image_name self_command 其中 -v: 将宿主的目录挂载到容器内部 -w: 指定工作目录 启动一个镜像(web应用,需要端口映射) docker run -it --rm -v $PWD:/tmp -w /tmp -p 5000:5000 self_image_name self_command 查看容器内部的标准输出 docker logs -f bf08b7f2cd89 查看容器内部运行的进程 docker top wizardly_chandrasekhar 查看容器的配置和状态信息 docker inspect wizardly_chandrasekhar 更新镜像 docker commit -m="has update" -a="runoob" e218edb10161 runoob/ubuntu:v2 打tag(push到仓库) docker tag 860c279d2fec runoob/centos:dev docker login xxxx.com docker push a/b/c/image_name:v1.0.0 GPU 环境安装 NVIDIA Docker 安装 如需在 Linux 上启用 GPU 支持,请安装 NVIDIA Docker 支持 验证 nvidia-docker 安装效果...

1 分钟 · pan

http2特性

HTTP协议发展历史 HTTP0.9 1991年发布。该版本极其简单,只有一个命令GET,不支持请求头 HTTP/1.0 1996年5月发布。引入请求头和响应头;新增请求方法,如head/post HTTP1.1 1997年1月发布。支持长连接;添加Content-Length字段;分块传输编码等 SPDY 2012年Google发布。HTTP2.0就是基于SPDY设计的,现在已经无人使用。添加多路复用(Multiplexing);header压缩(DEFLATE算法);服务端推送等 HTTP2.0 2015年发布。本文主要讲解内容,后文详细讨论。 HTTP3.0 2018年发布。尚未研究,不在本文讨论范围。 HTTP/2 的wiki介绍,可以看下定义和发展历史。RFC 7540 定义了 HTTP/2 的协议规范和细节, RFC 7541定义了头部压缩。如果有时间,最后就直接看RFC的文档。没有什么资料可以比官方文档写的更清楚。本文只是自已的归纳和整理。难免有些粗陋和错误,望评判指正。 一、HTTP2 解决什么问题 HTTP2的提出肯定是为了解决HTTP1.1已经存在的问题。所以HTTP1.1存在那些问题呢? 1.1 TCP连接数限制 因为并发的原因一个TCP连接在同一时刻可能发送一个http请求。所以为了更快的响应前端请求,浏览器会建立多个tcp连接,但是第一tcp连接数量是有限制的。现在的浏览器针对同一域名一般最多只能创建6~8个请求;第二创建tcp连接需要三次握手,增加耗时、cpu资源、增加网络拥堵的可能性。所以,缺点明显。 1.2 线头阻塞 (Head Of Line Blocking) 问题 每个 TCP 连接同时只能处理一个请求 - 响应,浏览器按 FIFO 原则处理请求,如果上一个响应没返回,后续请求 - 响应都会受阻。为了解决此问题,出现了 管线化 - pipelining 技术,但是管线化存在诸多问题,比如第一个响应慢还是会阻塞后续响应、服务器为了按序返回相应需要缓存多个响应占用更多资源、浏览器中途断连重试服务器可能得重新处理多个请求、还有必须客户端 - 代理 - 服务器都支持管线化。 1.3 Header 内容多 每次请求 Header不会变化太多,没有相应的压缩传输优化方案。特别是想cookie这种比较长的字段 对于HTTP1.1存在的这些问题,是有一定的优化方案的,比如用对个域名,文件合并等。但是这些毕竟比较麻烦,甚至无聊。 二、基本概念 数据流: 已建立的连接内的双向字节流,可以承载一条或多条消息。 消息: 与逻辑请求或响应消息对应的完整的一系列帧。 帧: HTTP/2 通信的最小单位,每个帧都包含帧头,至少也会标识出当前帧所属的数据流。 这些概念的关系总结如下: 所有通信都在一个 TCP 连接上完成,此连接可以承载任意数量的双向数据流。 每个数据流都有一个唯一的标识符和可选的优先级信息,用于承载双向消息。 每条消息都是一条逻辑 HTTP 消息(例如请求或响应),包含一个或多个帧。 帧是最小的通信单位,承载着特定类型的数据,例如 HTTP 标头、消息负载等等。 来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装。 三、HTTP2特性有那些 需要强调的是HTTP/2 是对之前 HTTP 标准的扩展,而非替代。 HTTP 的应用语义不变,提供的功能不变,HTTP 方法、状态代码、URI和标头字段等这些核心概念也不变。 我们已经知道http1....

4 分钟 · pan