admin

  • 标题:Gartner 数据库魔力象限发布:华为云、SingleStore进入;Yugabyte推出;Amazon RDS 支持 MySQL 9.1

    重要更新

    Gartner 正式对外发布了 2024 年的数据库魔力象限[1]:依旧由Google、AWS、Azure、Oracle领跑;MongoDB、DataBricks、Snowflake则又向左上角前进了一些;阿里云则依旧保持在领导者象限。华为云则在时隔两年后,再次进入该象限。分布式数据库厂商 SingleStore 进入,而 Yugabyte 跌出。

    更新详情

    GCP(谷歌云)
    • BigQuery 新增墨西哥地区支持 [7]
    • Cloud SQL Enterprise Plus 开始支持启用查询洞察[8]
    • AlloyDB 支持通过 Cloud Monitoring 信息中心监控以下众多指标 [13]
    火山云(字节)
    • 云数据库 veDB MySQL 版新增亚太东南 (柔佛) 地域售卖 [17]
    AWS(亚马逊云)
    • Amazon RDS 支持 MariaDB 11.4.4、10.11.10、10.6.20 和 10.5.27等小版本 [18]
    • Amazon RDS for Db2 支持多数据库能力 [19]
    • Amazon RDS 在数据库预览环境中支持 MySQL 9.1 [20]
    • Amazon RDS Proxy 新增 Aurora 和 RDS 上的 MySQL caching_sha2_password 身份验证支 [26]
    腾讯云
    • TDSQL-C MySQL、云数据库 MySQL 发布数据库代理版本1.3.15。[27]
    • TDSQL-C MySQL 版“只读分析引擎”发布了新版本 1.2404.19.0。在新的版本中修复了诸多内核问题,适配了周边生态组件。支持适配“数据库代理”,可通过数据库代理访问“只读分析引擎”。支持适配“DMC”,可通过新版 DMC(数据库管理)访问“只读分析引擎”。便于对只读分析引擎中数据的查询和使用 [29]

    参考链接

  • 本周 Gartner 正式对外发布了 2024 年的数据库魔力象限[1],对今年全球范围内大型数据库厂商做了一个整体的“盘点”,是的,在这个象限中几乎都是“大型”的数据库厂商。

    整体上,依旧有Google、AWS、Azure、Oracle领跑;MongoDB、DataBricks、Snowflake则又向左上角前进了一些;阿里云则依旧保持在领导者象限。华为云则在时隔两年后,再次进入该象限。

    分布式数据库厂商 SingleStore 进入,而 Yugabyte 跌出。

    领跑组:Google 高歌猛进

    Google 是 AI 与云计算领域的领导者,在数据库产品上不断增强与 AI 产品的链接;其他方向上,则加强其 AlloyDB (或其 Omni版本)、BigQuery、Spanner、Cloud SQL等功能。凭借着 AI 与 大数据技术的持续领先,Google 依旧是最领先技术的弄潮儿。

    Amazon 在数据库方向上的核心产品是托管数据库、Aurora 和 DynamoDB 。并且,在今年,Aurora 发布了 DSQL 版本,跨区域强一致的全球数据库,DynamoDB 也发布了类似的能力;托管数据库则不断紧跟社区,并开始以更高性价比的形式支持了最新一代的Graviton 芯片。

    微软在过去的数年,云计算成功的赶上了第一梯度,在 AI 浪潮中,微软凭借快速高效的与OpenAI进行合作并持续创新,再次站在潮头。在数据库方向,在云端微软一方面持续发展SQL Database、Cosmos DB。在本地则发布了SQL Server 2025版本。今年11月,看到 Azure 托管数据库发布支持了 PostgreSQL 17,追赶了这么年,可以认为 Azure 的数据库基础设施最终赶上了其他头部云厂商[3]

    Oracle 则在不断践行多云和 AI 战略,发布了Oracle@Google、Oracle@Azure、Oracle@AWS等系列合作产品。在 MySQL 方向上,依旧的,在不断的增强 HeatWave 能力,包括分析能力和 AI 功能[4]

    中国数据库厂商:阿里云和华为云

    在去年,中国数据库厂商,仅有阿里云数据库在孤军奋战[5] ,今年华为云再次进入该象限(注:2020/2021年曾进入),可见,华为在被美国限制的情况下,依旧在尝试在全球市场寻求更大的突破。阿里云相比于去年的位置,没有发生太大的变化,依旧是处于领导者象限。华为云,则相比于 2020、2021 年的所处的niche players象限进步很大,跃入了挑战者象限。

    而在魔力象限之外,依旧有不少数据库厂商在奋力征战全球市场。分布式数据库 TiDB 从市场宣传、产品投入可以看到,全球市场市场是其重点方向。向量数据库 Zilliz / Milvus,则已经站在了全球向量数据库的领导者的位置。此外,还有 NebularGraph 、Databend、KubeBlocks等。

    独立厂商与平台厂商

    在全球云计算快速侵蚀传统数据中心的大背景下,独立数据库厂商则在尝试寻找独立的、垂直的价值空间。其中一个非常重要的战略是,各个独立厂商都发布了各自的云服务平台,包括TiDB Cloud、Neo4j Aura、Redis Cloud、MongoDB Atlas等。

    关于 Gartner 魔力象限

    曾经在阿里云工作时,多次参加过 Gartner 数据库魔力象限的项目。Gartner 项目团队会从多个角度对数据库厂商进行评估,主要包括营收规模、多维度的产品能力、产品规划等方面,在评价体系中,Gartner 还会邀请厂商的客户对该厂商的产品进行评价。除此,厂商和 Gartner 项目组可以就自己关心的问题进行询问。最终,Gartner 会根据上述信息形成一个综合的评估,并将多个数据库厂商的评估结果汇总成一个整体的报告,也就是通常大家看到的,Magic Quadrant。

    在 Gartner 的 “Cloud Database” 定义是比较广泛的,不仅仅包含RDBMS,也包括各类NoSQL,此外,还包括了各个厂商的分析类产品。在计算营收时,通常云计算厂商会将数据库或大数据库类目的营收数据合并上报,所以规模通常都比较大。而且,云厂商的数据库营收,通常都是硬件(IaaS)营收为主,辅以部分授权收入,这也是云厂商收入规模很大的原因。相比之下,独立的数据库厂商,通常只能计算数据库售卖的授权费用,所以,在营收规模的维度,独立数据库厂商是难以与云厂商抗衡的。所以,第一梯队,甚至第一象限,几乎都是云厂商。

    另一面

    Gartner 更像是一个数据库的“神仙打架”榜单,Gartner的魔力象限有着非常高的准入门槛。入选 Gartner 最为重要的应该就是营收,有了营收,才有后面的所有,才有资源去做产品能力评估、规划汇报或者客户评价等。对于头部厂商来说,所有的资源都是充足了,厂商之间会在其他维度(诸如产品能力、客户评价等)去竞争,从而获得更好的象限位置,以便后续宣传。但,这对小的、创新厂商是不友好的。所有,对于没有进入的厂商,并不是这些厂商不优秀,也不是这些厂商不创新,而只是时候未到。

    过去十年对比参考

    参考链接

  • 标题:GaussDB MySQL更为TaurusDB,全新发布HTAP功能;SQL编程大赛决赛答辩周五晚举行;

    重要更新

    华为云 TaurusDB 新增HTAP实时分析(标准版)功能,能同时服务OLTP和OLAP的混合混合负载场景。其中AP部分采用列式存储引擎,利用SIMD计算技术提升实时分析性能[31]。TaurusDB 即为原 GaussDB(for MySQL) ,于11月更名 TaurusDB[32]

    12 月 14 日,Doris Summit Asia 2024 在深圳举办,该活动由飞轮科技主办,腾讯云和阿里云联合主办[1]。在12月07日,由 StarRocks 举办的 StarRocks Summit 在北京举办[2]

    更新详情

    阿里云
    • RDS MySQL基础系列常规实例支持免费的云盘加密功能,该功能将对整个数据盘上的数据进行基于块存储的加密,能够有效保障您的数据安全[4]
    • RDS SQL Server Web版独享入门规格实例支持大版本升级[5]
    Azure(微软云)
    • Azure Functions 支持 Azure Database for MySQL 绑定 [6]
    GCP(谷歌云)
    • Bigtable、 Memorystore for Redis 接入 Database Center 的支持,可让您集中查看整个数据库群 [8][9]
    • 数据库迁移服务现在支持 MySQL 次要版本 8.0.40 [10]
    • Spanner 现在支持“IDENTITY”列,“IDENTITY”列可让您自动为键列和非键列生成唯一的整数值,并与 ANSI 标准保持一致。[12]
    Oracle云
    • HeatWave:支持版本 9.1.2 版本 [16]
    AWS(亚马逊云)
    • Amazon DocumentDB(兼容 MongoDB)支持 NVMe 规格 R6gd [17]
    • Timestream for InfluxDB 现已支持 Internet 协议版本 6 (IPv6) 连接[23]
    • RDS for PostgreSQL 宣布扩展支持次要版本 11.22-RDS.20241121[28]
    腾讯云
    • 云数据库 MySQL 8.0内核版本更新20230703。[30]

    参考链接

  • 在昨天进行的SQL编程大赛中,所有 MySQL 选手的成绩都没有进入八强的。个人也对这个问题比较感兴趣,经过初步分析,重要的原因在于 MySQL 实现中没有比较好的并行加速的能力。而在 MySQL 的衍生版本中,倒是有几个版本提供了并行执行的能力。包括了 PolarDB 的 Elastic Parallel Query[2]Amazon Aurora 的 Parallel query[3] 。所以,也打算验证一下,如果加上这些并行能力,是否能够更快。

    结果综述

    PolarDB MySQL 运行了与 MySQL “几乎”(仅添加Hint开启并行)相同的SQL(参考)运行最快为:3.821 s。相比在同一个集群,不开启并行的时间是 6.647s,速度提升了 42.5% 。此外也测试了Aurora的相同的规格,几经调试依旧无法使用其并行能力。

    在相同的SQL实现下,PolarDB MySQL 可能是所有 MySQL 版本中性能最好的。如果,感觉还有什么版本可能有更好的性能,欢迎留言。

    数据与SQL

    这次是尝试使用 MySQL 高性能的完成“第二次SQL编程大赛”的进阶挑战。完整的题目描述可以参考:赛题说明[4]。这里实现的 PolarDB MySQL 版本 SQL 参考:gt.polardb.sql@GitHub[1](或参考本文结尾部分)。

    PolarDB的规格选择

    这里选择与试题类似的4c8g规格,详细参数如下:

    主要参数:

    • CPU架构:x86
    • 产品版本:企业版
    • 小版本号:8.0.2(与 MySQL 8.0.18 完全兼容)
    • IMCI只读节点个数:0
    • 初始只读节点个数:0
    • 初始读写节点个数:1
    • 节点规格:4 核 8GB(通用)
    • 存储类型:PSL5

    在 PolarDB 上并行执行

    PolarDB 的并行执行可以使用 Hint 较为方便的开启:

    SELECT
      /*+PARALLEL(8)*/
    ...

    可以通过执行计划观察,实际是否使用了并行:

    | -> Gather (merge sort; slice: 1; workers: 8)  (cost=2024861688.70 rows=1995124000) (actual time=1774.761..2159.495 rows=1000000 loops=1)
        -> Sort: <temporary>.p_id  (cost=1705198767.38 rows=249390500) (actual time=480.641,781.971,239.446..503.108,818.048,250.387 rows=125000,220305,60043 loops=1,1,1)
            -> Stream results (actual time=1.631,2.232,1.396..380.381,630.231,168.213 rows=125000,220305,60043 loops=1,1,1)
                -> Left hash join (t_01.a_s = p_01.a_s), (t_01.d_s = p_01.d_s), extra conditions: ((p_01.seq >= ((t_01.p_seat_to - t_01.seat_count) + 1)) and (p_01.seq <= t_01.p_seat_to))  (cost=25015432.30 rows=249390500) (actual time=1.621,2.220,1.387..200.048,332.885,86.841 rows=125000,220305,60043 loops=1,1,1)
                    -> Parallel table scan on p_01, with parallel partitions: 8 (actual time=0.002,0.003,0.002..48.149,74.839,20.489 rows=125000,220305,60043 loops=1,1,1)
                        -> Materialize (shared access, partitions: 8, partition_keys: a_s,) (actual time=0.001,0.002,0.001..24.240,34.098,9.331 rows=125000,220305,60043 loops=1,1,1)
                            -> Gather (slice: 1; workers: 8)  (cost=1146187.18 rows=997560) (actual time=158.204..362.897 rows=1000000 loops=1)
                                -> Window aggregate  (cost=1090064.43 rows=124695) (actual time=160.066,167.128,157.433..296.715,314.531,278.945 rows=125000,149658,102922 loops=1,1,1)
                                    -> Repartition (hash keys: passenger.departure_station, passenger.arrival_station; merge sort; slice: 2; workers: 8)  (cost=514229.38 rows=124695) (actual time=160.059,167.121,157.424..223.566,236.036,217.548 rows=125000,149658,102922 loops=1,1,1)
                                        -> Sort: passenger.departure_station, passenger.arrival_station  (cost=12554.66 rows=124695) (actual time=152.998,157.330,149.335..172.035,180.041,168.003 rows=125000,132932,111478 loops=1,1,1)
                                            -> Parallel table scan on passenger, with parallel partitions: 745 (actual time=0.052,0.058,0.046..57.559,65.907,54.511 rows=125000,132932,111478 loops=1,1,1)
                    -> Hash
                        -> Table scan on t_01
                            -> Materialize (shared access) (actual time=0.002,0.003,0.001..0.744,0.935,0.666 rows=2000,2000,2000 loops=1,1,1)
                                -> Gather (slice: 1; workers: 8)  (cost=3416.33 rows=2000) (actual time=6.884..7.646 rows=2000 loops=1)
                                    -> Window aggregate with buffering  (cost=3293.83 rows=250) (actual time=1.955,2.233,1.724..3.692,3.760,3.633 rows=250,292,196 loops=1,1,1)
                                        -> Repartition (hash keys: t_include_no_seat.d_s, t_include_no_seat.a_s; slice: 2; workers: 1)  (cost=3161.31 rows=250) (actual time=1.930,2.215,1.691..2.759,2.922,2.589 rows=250,292,196 loops=1,1,1)
                                            -> Sort: t_include_no_seat.d_s, t_include_no_seat.a_s, t_include_no_seat.if_no_seat, t_include_no_seat.t_id (actual time=1.383,1.383,1.383..2.070,2.070,2.070 rows=2000,2000,2000 loops=1,1,1)
                                                -> Table scan on t_include_no_seat (actual time=1.381,1.381,1.381..1.685,1.685,1.685 rows=2000,2000,2000 loops=1,1,1)
                                                    -> Materialize with deduplication (shared access) (actual time=0.001,0.001,0.001..0.295,0.295,0.295 rows=2000,2000,2000 loops=1,1,1)
                                                        -> Table scan on <union temporary>  (cost=2.50 rows=0) (actual time=0.001..0.290 rows=2000 loops=1)
                                                            -> Union materialize with deduplication  (actual time=2.330..3.003 rows=2000 loops=1)
                                                                -> Table scan on train  (cost=101.25 rows=1000) (actual time=0.050..0.380 rows=1000 loops=1)
                                                                -> Table scan on train  (cost=101.25 rows=1000) (actual time=0.023..0.348 rows=1000 loops=1)
     |

    这里的诸如Gather (merge sort; slice: 1; workers: 8)等内容,显示对应的部分会通过多线程并行执行。

    执行时间统计

    这里运行了该 SQL 三次的结果统计如下:

    real	0m3.863s
    user	0m0.379s
    sys	0m0.100s
    
    real	0m3.917s
    user	0m0.414s
    sys	0m0.123s
    
    real	0m3.821s
    user	0m0.422s
    sys	0m0.128s

    说明:这里,因为PolarDB是运行在云端,故仅需关注这里的 real 部分的时间。

    不开启并行时 PolarDB 的性能

    该组数据可用对比:

    real	0m6.743s
    user	0m0.394s
    sys	0m0.120s
    
    real	0m6.647s
    user	0m0.393s
    sys	0m0.120s
    
    real	0m6.665s
    user	0m0.407s
    sys	0m0.125s

    并行执行的一些状态参数

    mysql> show global status like '%pq_%';
    +-------------------------------------+-------+
    | Variable_name                       | Value |
    +-------------------------------------+-------+
    | PQ_fallback_one_worker              | 0     |
    | PQ_local_workers_created            | 297   |
    | PQ_migrant_workers_created          | 0     |
    | PQ_net_exchange_fail_connect        | 0     |
    | PQ_refused_over_computing_resource  | 0     |
    | PQ_refused_over_max_queuing_time    | 0     |
    | PQ_refused_over_total_workers       | 0     |
    | PQ_remote_workers_created           | 0     |
    | PQ_running_local_workers            | 0     |
    | PQ_running_migrant_workers          | 0     |
    | PQ_running_remote_workers           | 0     |
    | PQ_sched_adative_resource_dec_count | 0     |
    | PQ_sched_adative_resource_inc_count | 0     |
    +-------------------------------------+-------+

    使用 Aurora 的并行执行

    这里也尝试使用 Aurora 的并行查询进行优化,但是并没有成功。Aurora 的并行执行并没有 Hint 可以控制,而是优化器根据需要选择使用。在本次测试中,在一个 4c32gb 的Aurora实例上,几经尝试,都未能实现并行。故未成功测试。在非并行时,Aurora的执行时间为 6.921s。

    开启Aurora并行执行

    要使用 Aurora 的并行执行能力,需要先创建最新版本的Aurora,在选择的参数组(parameter group)时,该参数组需要打开 aurora_parallel_query 参数。在实例创建完成后,可以通过如下命令查看并行查询是否打开:

    mysql> show global variables like '%aurora_parallel_query%';
    +-----------------------+-------+
    | Variable_name         | Value |
    +-----------------------+-------+
    | aurora_parallel_query | ON    |
    +-----------------------+-------+
    Aurora上的执行时间

    这里记录相关SQL的执行时间如下:

    real	0m6.921s
    user	0m1.022s
    sys	0m0.076s
    [ec2-user@xterm-256color- delete_me]$ time mysql --local-infile=true -hpq-testing.cluster-cjzowaj9vqpd.ap-northeast-1.rds.amazonaws.com -ub_admin -p-f7HNhmp_frX game_ticket < aurora.sql > aurora.ret
    mysql: [Warning] Using a password on the command line interface can be insecure.
    
    real	0m6.955s
    user	0m1.012s
    sys	0m0.076s
    [ec2-user@xterm-256color- delete_me]$ time mysql --local-infile=true -hpq-testing.cluster-cjzowaj9vqpd.ap-northeast-1.rds.amazonaws.com -ub_admin -p-f7HNhmp_frX game_ticket < aurora.sql > aurora.ret
    mysql: [Warning] Using a password on the command line interface can be insecure.
    
    real	0m7.154s
    user	0m0.970s
    sys	0m0.138s

    最后

    PolarDB MySQL 在并行执行开启的情况下性能提升了42.5%,最终执行时间为 3.821 s。有可能是所有 MySQL 兼容的发型版本中性能最快的。

    gt.polardb.sql

    -- explain analyze
    WITH
      t_no_seat_virtual AS (
        select
          train_id as t_id,
          departure_station as d_s,
          arrival_station as a_s,
          seat_count,
          seat_count*0.1 as seat_count_no_seat
        from train
      ),
      t_include_no_seat AS (
        select t_id,d_s ,a_s ,seat_count, 0 as if_no_seat
        from t_no_seat_virtual
        union
        select t_id,d_s ,a_s ,seat_count_no_seat, 1 as if_no_seat
        from t_no_seat_virtual
      )
    SELECT
      /*+PARALLEL(8)*/
      p_01.p_id,         -- output 01
      p_01.d_s,          -- output 02
      p_01.a_s,          -- output 03
      t_01.t_id as t_id, -- output 04
      IF(
          if_no_seat,
          "" ,
          ceil((p_01.seq-t_01.p_seat_to + t_01.seat_count)/100)
      ) as t_carr_id, -- output 05
    
      CASE IF( !isnull(t_01.t_id) and if_no_seat,-1,ceil((( p_01.seq-t_01.p_seat_to + t_01.seat_count )%100)%5))
        WHEN 1  THEN CONCAT( ceil((( p_01.seq-t_01.p_seat_to + t_01.seat_count )%100)/5) ,"A")
        WHEN 2  THEN CONCAT( ceil((( p_01.seq-t_01.p_seat_to + t_01.seat_count )%100)/5) ,"B")
        WHEN 3  THEN CONCAT( ceil((( p_01.seq-t_01.p_seat_to + t_01.seat_count )%100)/5) ,"C")
        WHEN 4  THEN CONCAT( ceil((( p_01.seq-t_01.p_seat_to + t_01.seat_count )%100)/5) ,"E")
        WHEN 0  THEN CONCAT( IF( (p_01.seq-t_01.p_seat_to + t_01.seat_count)%100 = 0, "20" ,ceil((( p_01.seq-t_01.p_seat_to + t_01.seat_count )%100)/5)) ,"F")
        WHEN -1 THEN "无座"
        ELSE NULL
      END as seat_index   -- output 06
    FROM
      (
        select
          /*+PARALLEL(8)*/
          ROW_NUMBER() over(PARTITION BY departure_station,arrival_station) as seq ,
          passenger_id as p_id,
          departure_station as d_s,
          arrival_station as a_s
        from
        passenger
      ) as p_01
    
      LEFT JOIN
    
      (
        select
          /*+PARALLEL(8)*/
          seat_count,
          sum(seat_count)
            over (
                   PARTITION BY d_s,a_s
                   ORDER BY     if_no_seat,t_id
                 ) as p_seat_to ,
          t_id,
          d_s ,
          a_s ,
          if_no_seat
        from
        t_include_no_seat
      ) t_01
    
      ON
            p_01.seq >= p_seat_to-seat_count + 1
        and p_01.seq <= p_seat_to
        and p_01.d_s =  t_01.d_s
        and p_01.a_s =  t_01.a_s
    ORDER BY p_01.p_id

    参考链接

  • 随着企业规模增长,或者,企业原本就在监管非常严格的行业或环境,所面临合规要求也会逐步增多,密码的定期轮换通常也是众多要求中的一个基础项。MySQL 8.0 提供的“双密码”管理机制,可以让大规模数据库密码轮换变得更平滑,而无需任何的停机时间。

    100台MySQL、1000台应用服务

    考虑这样的场景,你是一个 DBA ,生产环境有 100 台 MySQL 实例,运行在这些数据库上约有 1000 台应用服务器。如果需要对,这些 MySQL 的应用账号密码统一做一次更新,如何实现不停机的迁移。

    在 MySQL 之前(8.0.14版本之前)的单密码机制下,更换密码通常会遇到以下问题:无论是先更新数据库服务器上的密码,还是新调整应用服务器上的密码,这个先后顺序都会带来一定的服务不可用时间。通常,为了降低这个服务不可用时间,应用变更人员和 DBA 则需要非常密切的配合。

    双密码机制(dual password)

    从 MySQL 8.0.14 起,开始支持了双密码策略。在密码更换时,可以保留旧密码依旧可用:

    ALTER USER 'appuser1'@'host1.example.com'
      IDENTIFIED BY 'password_b'
      RETAIN CURRENT PASSWORD;

    在一段时间后,可以删除旧密码:

    ALTER USER 'appuser1'@'host1.example.com'
      DISCARD OLD PASSWORD;

    构建批量密码轮换流程

    有了该功能,就可以使用上述的命令,更加平滑的实现大规模数据库场景下的密码更换。

    1. 给变更数据库服务器上账号的密码,同时将原密码作为备用密码保留(使用“RETAIN CURRENT PASSWORD”)
    2. 变更应用服务器上的访问数据库的密码
    3. 检查并观察一段时间,删除数据库中的原密码(使用“DISCARD OLD PASSWORD ”)

    这是一个简化的过程,实际的操作还需要考虑:

    • 严格遵循企业内部的变更规范
    • 灰度的进行发布,而不是一次性操作所有的实例/服务器
    • 做好检查,需要确保诸如密码已经变更完成、应用服务器密码均已经更新
    • 做好回滚方案,避免过程中操作失误导致的问题
    • 在业务低峰时间进行操作

    参考阅读

  • Mini-batch Gradient Descent的主要想法

    在前面的实践中,使用“Gradient Descent”时,都是一次性把所有的数据集都考虑进去,一切似乎都没有什么问题。

    但是,在现实实践中,如果训练数据量非常大,这种方式就不适用了。试想,在前面的示例中,输入样本数据,即\( X \)(或者 \( A^{[0]}\)),其维度(或者说shape)则可能是\( 300 \times 10,000,000 \)。这会导致每一次的梯度计算量都非常大,一次迭代(epoch)的时间会很长。

    一种非常常见的、也是非常经典的梯度下降改进,就是 mini-batch gradient descent。首先,将样本分层若干小份,并单独对每个“小份”进行“Gradient Descent”,最后逐步完成所有样本的训练。完成一次所有样本的遍历,称之为一次迭代epoch

    在神经网络的训练中,可以用下面步骤理解这个过程:

    for "a-small-batch-of-samples X^{t} " in "all-samples"
        forward  propagation on X^{t}
        backward propagation on X^{t}
        update W/b
            W^{[l]} = W^{[l]} - lr * dW^{[l]}
            b^{[l]} = b^{[l]} - lr * db^{[l]}

    相对于 mini-batch gradient descent ,前面介绍的一次把所有样本都用于训练的方法,也被称为“batch gradient descent”。

    Stochastic Gradient Descent

    在 mini-batch 中,如果每次批量的样本大小是 1 的话,那么,也称为Stochastic Gradient Descent(简称 SGD,随机梯度下降)。事实上,自开始使用 Backpropagation 算法以来,SGD就被用于提升神经网络的训练的效率。并且很快的,mini-batch gradient descent 就作为一种优化被使用。目前,mini-batch gradient descent 依旧是一种常用神经网络训练方法[1][2]

    收敛稳定性预估

    不难想象,在“batch gradient descent”中,一次性把所有数据都用于梯度下降的算法,通常都能够获得更好的收敛速度。而在使用 mini-batch 的时候,由于不同的批次的样本在计算时,“计算梯度”都与“全局梯度”有一定的偏差,并且有时候偏差较大,有时候较小,所以,相比之下,收敛速度要慢一些。而,Stochastic Gradient Descent 则可能会更慢一些。

    可以这么理解, mini-batch 和 Stochastic Gradient Descent 都总是使用一个局部的梯度(部分样本的梯度)来预估整体的梯度方向,自然效果会差一些。

    这里我们改进了原来的神经网络实现 ssnn_bear.py,将其更新为使用 mini-batch 的 ssnn_cat.py。详细代码可以参考GitHub仓库:super simple neural network

    运行 ssnn_cat.py 并观察 mini-batch 的 cost 下降速度如下:

    mini-batch 迭代下cost总是在上下波动

    batch gd算法下cost下降非常稳定

    batch_size 大小的配置

    一些经验数值可能是64、128、256、512等。Mini-batch 需要在大数据量和向量化计算之间取得一个平衡,所以通常需要更具训练的设备选择合适的 batch 大小,使得每次迭代都充分利用硬件的资源。

    代码实现

    在前述的神经网络上(参考),再做了一些修改,实现了 Mini-Batch 或者随机梯度下降。

    新增 batch 大小参数

    首先,新增了 batch_size 参数表示,同时增加对应参数 batch_iteration 表示,在一次样本迭代中,总计需要循环多少次。所以,这两个参数有如下关系:

    batch_iteration = math.ceil(m/batch_size)
    对每批次样本做迭代
    batch_iteration = math.ceil(m/batch_size)
    ...
    for i in range(iteration_count):
        for i_batch in range(batch_iteration):
            ...
            # sample from i_batch*batch_size
            batch_from  = i_batch*batch_size
            batch_to    = min((i_batch+1)*batch_size,m)
            batch_count = batch_to - batch_from
            A0  = X[:,batch_from:batch_to] # column  [batch_from,batch_to)
            Y_L = Y[:,batch_from:batch_to] # Y_label [batch_from,batch_to)
            ...

    代码的其他地方,几乎不需要太大的改动。完整的代码参考:ssnn_cat.py

    参考

    • [1] Stochastic gradient descent
    • [2] https://github.com/orczhou/ssnn/blob/main/ssnn_cat.py
    • [3] https://github.com/orczhou/ssnn/blob/main/ssnn_bear.py
    • [4] https://github.com/orczhou/ssnn

    这是一个系列文章包含了,完整的文章还包括: