C++笔记
简介:当我不断用Kimi写C++而一直报错使得我一下午都没有达成目标时,我意识到我不能在自己不懂C++的情况下完全交给AI去写代码,我至少得看得懂C++代码,以免AI只是在糊弄我。当我会C++之后,我可以用AI来减少重复,但我不可能对C++一知半解的情况下完成工作目标。
补全基础知识
简介:基础不牢,地动山摇!这些年上的学,四年生物医学工程本科,三年电子信息(仪器仪表工程)硕士,着实让我没学到太多用得上的知识,现在工作最用得上的就只有上学时学的一点点编程的知识。想到这里,不禁感到痛心,我这些年并不是混日子,而且付出很多努力学习也很辛苦,但是奈何学到的东西大多没有用到,而我的光阴、斗志、兴趣、热情也都在这七年的时间中慢慢磨没了,怎能不让人痛心呢!工作之后,我意识到只有十年坚持一项事情、一个领域才可能在现实中找到立足之地,要是前面七年的东西能够用上,我的原始积累就有底气了,可惜没有。既然到了这个地步,就不要再纠结过去了,立足当下,忘掉过去,构建未来。
自学大纲
- 英语:我这些年,人生的重点关键点就输在英语面试上,比如2019年中科大保研面试惨败,2023年申请香港优才分不够,2024年南科大申博面试英语口语惨败…等等。它是我此生的痛!
- 计算机基础:
- 数据结构与算法
- 操作系统
- 计算机组成原理
- 计算机网络
- 编译原理
- 计算机体系结构
- C/C++
- Python
- Java
- 芯片基础:
- SoC设计方法
- FPGA
- Verilog
- AI基础:
- 深度学习
GPGPU-sim使用
简介:学一下GPGPU-sim,也许比Gem5好重构。
考驾照
简介:一直很懒没去考驾照,记录自己考驾照的知识点,逼自己考完。
科目一
1.扣分、罚款、判刑
- 1 3 6 9 12分,违法行为记几分?
- 20 200 和 2000,罚款罚多少?
- 驾驶证申领、审验、满分学习
- 判刑判几年?
01 扣分
- 取消2分,新增9分。选项有2分直接排除
- 1分关键词:会车、普倒掉、使用灯光、年检、安全带、禁令标志标线
- 使用灯光:不按规定 1分、故障双闪没开 3分、信号灯闯红灯 6分
- ..以外的机动车 –> 普通车; (高速公路、城市快速路)..以外的道路 –> 普通路 * 3分关键词:普逆、手持电话、前方排队穿插占道、高速低于最低速度(最低60)、高度不按车道行驶、故障停车且不按规定使用灯光或警告标志、三不让(让行、让行人、让校车)
- 高逆 12分
- 凡事看到选项“让”了的,基本是对的 * 6分关键词:应急车道、信号灯、暂扣期间驾驶、不构成犯罪肇事逃逸且轻微伤及财产损失、危险爆炸品未挂警示标或进入限行区
- 构成犯罪的肇事逃逸,严重的吊销驾照
- 不构成犯罪的肇事逃逸,轻微伤以上及死亡,12分 * 9分关键词:驾驶准驾车型不符、高速城快违法停车、无校车资格驾驶校车 * 12分关键词:饮酒、高倒掉逆、替人接收处罚消分牟取利益、不构成犯罪的肇事逃逸轻微伤以上及死亡
- 醉酒,吊销驾照,几年内不得考 * 号牌扣分
- 不按规定安装 3分
- 未挂或故意遮挡 9分
- 伪造、变造 12分 * 9种超速扣分
车辆 超速 普路 高速/城快路 普通车 20%~50% 3分 6分 普通车 50%以上 3分 12分 校中危 10%~20% 1分 6分 校中危 20%~50% 6分 12分 校中危 50%以上 9分 12分 - 高速:只有6 12
- 普路以外、普车以外,均6分起
- 20%~50%,非普路或者非普车,均6分起 * 7种超员扣分总结
超过核定人数 7座以下 7座以上 20% ~ 50% 3分 6分 50% ~ 100% 6分 9分 - 7下3 6,7上6 9;
- 超了100%的都是12分;
- 校公旅分两档:20%以下6分,20%以上12分 * 货车超载 1 3 6
- 30%以下,1分
- 30%~50%,3分
- 50%以上,6分
02 罚款
- 200元以下找车相关:
- 转让:转让后,30日内为办理转让登记
- 车身:改变车身颜色、车身广告
- 安检:未按规进行安全技术检验
- 20-200元罚款关键词
- 补领新证用原证
- 实习期单独上高速(实习期需要三年以上老司机陪同、不能开牵引挂车)
- 驾驶人,信息变更30内未备案
- 让撤不撤造成堵塞,罚款200
- 200-500元罚款关键词
- 逾期不审验仍驾驶(初次申领6年)
- 隐瞒、欺骗补领驾驶证
- 身体不适合仍驾驶
- 公路客运超员未达20%(超过20%罚款500-2000元)
- 200-2000元罚款关键词
- 驾驶拼装、报废车(并吊销驾驶证、并非行驶证)
- 超速50%(并吊销驾驶证)
- 把车交给无证人开
- 非法安装报警器
- 500,2000元罚款关键词
- 虚假材料500:虚假材料未取得驾驶证,1年内不得申领,若取得,则撤销且三年内不得申领
- 考试贿赂2000
- 审验相关处罚
- 弄虚作假,1000以下
- 代替他人,2000以下
- 组织他人,3倍以下,最高2万
- 代记分:
- 请他人代记分于支付罚款的、以及代替接受记分牟取利益的,处3倍以下罚款,最高不超5万
- 组织实施前两款行为,处5倍以下罚款,最高不超过10万
- 酒驾罚多少钱
- 饮酒(20-80mg/100ml):暂扣6个月,扣12分,罚款1000-2000
- 再次饮酒:吊销,拘留10日以下,罚款1000-2000
- 醉酒(>= 280mg/100ml):约束至酒醒,吊销5年
- 只要发生重大事故构成犯罪,终生不得申领驾驶证
03 驾驶证
- 驾驶考试要求:
- 科一、科二、科三(道路驾驶技能、安全文明驾驶常识)
- 学习驾驶证明有效期3年
- C1:30日预约科三,C2:20日后预约科三
- 科二科三预约次数分别不得超过5次
- 常考准驾车型
- 年龄限制三挡:18 20 22,除了C1、C2,上限都是60
- C1还可以驾驶C2,C3,C4
- 自动挡C2只能驾驶自动挡
- B1科技驾驶C1、C2、C3、C4
- 客车、牵引车不得初次申
- 轻牵挂20,重牵挂22
- 吊销情况
- 假1:虚假材料申领(未取得),1年
- 吊2:一般情形吊销后,2年
- 撤3:非法取得,撤销驾照,3年
- 醉5:醉酒驾驶非营运车(私家车),5年
- 逃终生:肇事逃逸,构成犯罪的,终生
- 补正、换证去哪
- 车相关(号牌、行驶证),找登记地
- 人相关(驾驶证),找核发地(以及核发地以外)
- 驾驶证审验:
- 需要审验情形:①记分周期结束30日内;②有效期满换证;③延期审不超过3年
- 不需审验情形:①机动车检验情况;②车辆累计里程
- 实习期
- 不得单独上高速,3年或以上老司机陪同
- 不可参加学法减分
- 实习期12个月挂统一样式实习标识,记满12分注销
- 驾驶证时间
- 有效期:6年-10年-长期(中间不能扣满12分)
- 信息变更30日,有效期满90日
- 申请增加准驾车型(记分周期不满12分+年限)
- 轻1:轻型牵引车C6。1个记分周期+小型汽车(C1)或小型自动挡汽车(C2)满1年
- 中2:中型客车B1。2个记分周期+小型汽车C1满2年
- 大3:大型客车A1。3个记分周期+大型货车B2满3年(或城市公交车、中型客车2年,或重型牵引挂车1年)
- 机动车注册、变更登记
- 变更登记:改变车身颜色、改装车架、改装发动机、更换整车
- 不予注册登记:参数不符、报废、查封扣押、盗抢骗
- 加装防撞不需要变更登记
- 上路必带两证两标一号牌
- 驾驶证、行驶证、检验合格标志、保险标志、号牌
- 扣行驶证都是错的
- 扣车:①未带和伪造变造两证两标一号牌;②拼装报废、超载;③未投保交强险、调查取证
- 满分教育
- 满12分:法规(科一)
- 满24分:法规(科一)、道路(科三)
- 满36分:法规(科一)、场地(科二)、道路(科三)
- 记满12分,学7天;每加12分,多7天,最多60天
- 学法减分(未满12分,周期12个月,最高减6分)
- 公益111:1小时 1分
- 现场112:1小时 2分
- 网上331:30分钟 1分(3日内)
04 责任判定
- 危险驾驶、交通肇事罪
- 危险驾驶罪:追逐竞驶、醉驾(处拘役并处罚金)【还未造成后果,拘役】
- 交通肇事罪:酒驾毒驾、无牌报废、肇事逃逸【有发生后果,判刑】
- 做题:交通肇事 选除了3个“未”以外的
- 赔偿责任
- 车对车:①故意伪造现场–>全责;②货车散落货物致货车撞上–>货车全责
- 车对人:①车无错–>车承担不超过10%的赔偿;②人故意或车静止–>车不承担赔偿
- 判刑判几年
- 未逃逸:3年以下
- 死亡后逃逸:3-7年
- 逃逸致死亡:7年以上
2.通行原则、标志标线
- 通行原则:谁让谁,谁先走
- 特殊路况:山区陡坡、冰雪天如何操作
- 灯光仪表、事故处理、医疗常识
- 信号灯、交警手势、标志标线
- 核心口诀
- 右让左、弯让直、右方先行
- 禁止停车:口五站三
- 加速、迅速、随意、空挡滑行都是错的
- 减速、缓慢、避让都是对的
- 一停二看三通过
- 会车减速靠右,遇人停车减速
01 通行原则
- 会车让行题-谁先走?
- 右让左、弯让直、右方先行
- 有障碍让无障碍
- 环岛外让环岛内
- 靠山让临崖
- 特殊车辆先行
- 下坡让上坡,但下坡下到一半,下坡先行
- 超车
- 禁止超车的情形
- 前车转弯掉头超车
- 特殊车辆
- 特殊路段
- 无超车条件、不让超
- 会车可能
- 只能在左侧超
- 变换远近光灯火鸣喇叭示意
- 尽快超越,减少并行时间
- 驶回提前打右转
- 被超越,减速靠右让
- 禁止超车的情形
- 禁止停车
- 人行横道、施工路段
- 禁停标志标线
- 口5:交叉路口、铁路道口、隧道、急弯、窄桥、上陡坡下陡坡(50米内不给停车)
- 站3:公交站、加油站、消防站(30米内不给停车)
- 变道:
- 依次行驶
- 不得频繁变更车道
- 每次只能变更一条车道
- 实线不可跨,虚线可跨
- 不可倒车
- 高速公路、隧道、桥梁
- 铁路道口、交叉路口
- 急弯、陡坡、单行路
- 易错题-校车让行
- 三种情况:同向一车道、同向两车道、同向三车道
- 口诀:一二车道停,三车道最左侧减速
02 复杂路段
- 避开特殊路上的坑
- 上坡:①提前减档、踏加速踏板;②坡顶鸣喇叭示意
- 下坡:①不得空挡熄火滑行;②挂低速挡,发动机制动
- 山区:①减速,鸣喇叭,靠右行;②坡长急弯、视距不足
- 铁路道口:①一停二看三通过;②中途不得换挡
- 环岛:①按逆时针方向行驶;②进不打灯,出开右转
- 隧道:①近光灯/示廊灯;②禁止停倒掉超
- 过水路面:①先停车察明水情;②确认安全后低速通行;③间断轻踏恢复效能
- 泥泞路段:①容易侧滑、甩尾;②向侧滑的一侧修正;③平整坚实路段通过
- 坡道-停车方向盘题
- 上坡左转,下坡右转
03 特殊天气
- 雨天
- 路面湿滑
- 行驶阻力减小
- 刚开始下雨路最滑
- 雨刮刮不干净,靠边停车
- 雾天
- 雾天开雾灯
- 不用远光
- 浓雾看不清时选择安全地点停车
- 雪天
- 制动距离延长
- 防滑链装在驱动轮,循车辙行驶
- 前车通过后再上坡
- 积雪对光反射,造成驾驶人眩目
- 大风天
- 大风车不得超车
- 握稳转向盘
- 来风方向修正方向盘
- 关键词答题法:
- 错误的关键词:急转方向、紧急制动(非必要不急刹)、加速、雾天远光(基本不开远光灯)
- 正确的关键词:安全间距、发动机制动、稳、鸣喇叭提醒
- 看不清 –> 安全地点停车
- 临时停车 –> 危险报警闪光灯
04 速度
- 标志:红高、蓝低、黄建议
- 地面标线:黄高、白低
- 无线:城3公4;有线:城5公7 (指车道中间知否有线)
- 特殊速度找30:
- 特殊路段:非机动车道、铁路道口、急弯陡坡、窄路窄桥、冰雪路、泥泞路
- 特殊天气:雾、雨、雪、沙尘、能见度50米内
- 牵引故障车
- 高速公路行车速度
- 小车最高120,货车最高100
- 高速各车道最低速度:
- 同向两车道:100 60
- 同向三车道:100 90 60
- 同向四车道:100 90 90 60
- 高速限车距离:
- 车速超过100Km/h,车距>100m
- 车速低于100Km/h,车距>=50m
-
高速低能见度速度
口诀 能见度 车速 跟车距离 261 <200m <=60Km/h >=100m 154 <100m <=40Km/h >=50m 520 <50m <=20Km/h 尽快驶离
05 灯光、仪表
- 近光灯:夜间起步、跟车、交叉路口、隧道
- 远光灯:(非必要不开远光)只有夜间照明不良的路能开远光,会车150米以外改成近光灯
- 危险报警闪光灯:(临时停车必须开)牵引故障车、车辆发生故障、低能见度
- 雾天开雾灯
- 远近交替:(示意)夜间急弯、坡道、人行横道夜间没有信号灯控制的路口/超车/提醒对向来车关闭远光
- 仪表:①发动机转速表 X1000r/min;②速度和里程表 km/h;③燃油表;④水温表
- 个位数找转速
- 三位数找速度
- 加油站找燃油
- 温度计找水温
- 四个常开照明信号
- 近光灯斜线,远光灯平线
- 烤串形状–>前雾灯、后雾灯
- 前后位灯/示廊灯(对称)
- 危险报警闪光灯(红三角)
- 操纵装置
- 左灯右水,左-灯光信号组合,右-雨刮组合
- LOCK:切断电源,锁定方向盘
- ACC:接通附件电源
- ON:接通发动机外所有电源
- START:启动发动机
- 左边:离合,中间:制动,右边:加速踏板(自动挡没有离合)
- 简单仪表指示灯:
- 行李舱盖开启
- 发动机罩开启
- 两侧车门未关闭
- 空气内循环
- 空气外循环
- 车灯总开关
- 冷风暖气风扇
- 没系好安全带
- 前风窗玻璃刮水器及洗涤器
- 后风窗玻璃刮水器及洗涤器
- 前风窗除雾
- 后风窗除雾
- 迎面吹风
- 地板及迎面吹风
- 车门锁住车锁开关
- 易错仪表指示灯
- 问温度就是高,其它都是过低/不足
- 冷却液不足
- 发动起温度过高/冷却液不足
- 机油压力过低或机油量不足(滴油的形状)
- 燃油已到最低液面(加油站的形状)
- 问状态,只有P工作在制动,其它都是故障异常
- 驻车制动器处于制动状态(P标志)
- 制动系统出现异常或故障(!标志)
- ABS系统故障
- 充电电路故障
- 发动机控制系统故障
- 安全气囊处于故障状态
06 事故处理
- 问温度就是高,其它都是过低/不足
- 爆胎题
- 胎压过高/过低都会爆胎
- 先稳定方向再减速,非必要不急刹
- 爆胎原因:磨损、超载、尖锐物、胎压
- 爆胎预防:定期检查、清理异物、更换
- 正确处理:稳方向、发动机制动、平稳停车
- 故障停车4考点
- 打灯,危险报警闪光灯
- 放标,车后放警告标志(高速150米以外,普通路50-100米)
- 人转移,高速上转移到路肩外
- 报警,找救援或拖曳车
- 自己协商还是报警
- 只有无伤亡、无争议才能自行协商;人没问题、车没问题才能自行协商,其它只能报警
- 让你撤你不撤,造成拥堵罚款200
- 错误选项:无意义的非得等保险公司、等交警
- 撞了公共设施要报警
07 现场急救
- 急救-先救命后治伤
- 失血:先止血、动脉出血按压近心端、包扎不用麻绳(有出血必先止血)
- 昏迷:昏迷先检查呼吸、搬运昏迷先开放气道、胸外按压100-120
- 骨折:三角巾固定、不随意移动、不用软担架 (不能扶着走)
- 烧伤:烧伤喷冷水、保鲜膜覆盖、喝淡盐水
08 信号灯
- 易错四个点:
- 绿灯,路口拥堵也不得进
- 直绿灯左红灯,可进左弯待转区
- 黄灯交通信号暂时解除(没有交通信号了),单黄灯闪:路口警示
- 圆形信号灯右转不受限
09 交警手势
- 停止信号:左高右低
- 直行信号:双手从未放下
- 左转弯待转信号:单左手下压
- 减速慢行信号:单右手下压
- 变道信号:单右手向中间摆动
- 右转弯信号:左手掌心朝前,右手在下
- 左转弯信号:右手掌心朝前,左手在下(看见臂章选左转弯)
10 标志
- 核心口诀:
- 警告:
- 找注意,一急二反三连续
- 有山是傍山险路,有山有石找落石
- 栏杆有人,火车头无人
- 禁止:
- 红色禁止,黑色解除
- 三减八停
- 正脸机动车,侧身对应车型
- 指示:
- 圆直行,方单行,虚车道
- 顶角指明通行方向
- 会车,哪边箭头粗哪边先走
- 警告:
- 警告标志:
- 两侧变窄 vs 窄桥
- 无人看守铁路道口 vs 有人看守铁道路口(一道杠50米)
- 注意保持车距 vs 注意潮汐车道
- 注意野生动物 vs 注意牲畜
- 易滑路段 vs 堤坝路
- Y形交叉路口 vs 注意合流
- 急转弯路 vs 反向弯路 vs 连续弯路
- 减速丘/路面高突 vs 路面不平 vs 驼峰桥
- 傍山险路 vs 注意落石
- 下陡坡 vs 连续下坡
- 事故易发(没有追尾标志)
- 禁令标志:
- 禁止超车 vs 接触禁止超车
- 禁止机动车驶入 vs 禁止小型客车驶入
- 禁止停车 vs 禁止长时停车
- 停止通行 vs 禁止驶入
- 减速让行(三角形) vs 停车让行(八边形)
- 限制宽度 vs 限制高度
- 易混标志
- 只准直行 vs 单行路 vs 直行车道
- 左侧通行 vs 右侧通行 vs 两侧通行(顶角指明通行方向)
- 双向交通 vs 会车让行 vs 会车先行 vs 路口优先通行(哪边粗哪边先走)
- 错车道 vs 紧急停车带
- 停车区预告 vs 服务区预告
- 救援电话 vs 紧急电话
- 注意行人 vs 人行横道
- 机动车行驶 vs 机动车车道
- 禁止非机动车进入 vs 禁止电动自行车进入
- 新标志:
- 电动汽车充电站
- 开车灯
- 非机动车与行人通行
- 靠右侧车道行驶
- 交通事故管理
- 注意积水
11 标线
- 白色同向,黄色对向。潮汐车道双黄虚线
- 实线不可跨,虚线可跨。黄色虚线在保证安全的前提下,可以越线超车或掉头
- 左弯待转区线、路口导向线、网状线内不能停车
- 横向减速标线、纵向减速标线、可变导向车道线(有锯齿形)
- 禁止掉头标记、左弯或需向左合流、左转、人行横道预告、中心圈(不得压线行驶 )
- 限时停车位、固定方向停车位、平行式停车位(蓝色免费,白色收费,黄色专属)
- 三车道变两车道渐变段标线、接近障碍物标线、立面标记
- 路缘石上黄色实线–禁止停车;黄色虚线–禁止长时间停车
3.临考急救
- 扣分题的关键词
- 1分:普倒掉、会车、灯光、安全带、年检、禁令标
- 3分:普逆、电话、排队穿插、故障灯光、三不让
- 6分:应急、信号灯、暂扣、逃逸轻微
- 9分:不符、高违停、无校资
- 12分:饮酒、高倒掉逆、买卖分、逃逸死亡
- 罚款题
- 假审1代审2组3倍,高2万
- 代记分三五倍,3倍5,5倍10
- 假材料500,考试贿赂2000
- 让撤不撤造成拥堵,罚款200
- 饮酒扣,再饮吊,罚1000-2000
- 醉驾吊5年,罪终生
- 200以下:转让、车身、安检
- 20-200:实习期、用原证
- 200-500:逾期,隐瞒补领
- “三超”的扣分:
- 超速:①以外找6分;②20%-50%找高速,有高6,无高3;③高速只有6,12
- 超员:7以下3,6;7以上6,9,;校公旅6,12
- 超载:货车超载1,3,6
- 号牌:不按3,未挂9,伪造变造12
- 做题口诀
- 拼装报废超50,200-2000处吊销
- 先稳方向再减速,非必要不急刹
- 行人过马路,选项找停车
- 口5站3(有站找3,没站找5)
- 无线城3公4,有线城5公7
- 高速低能见度261/145/520
- 假1吊2撤3醉5逃终生
- 客牵不得初次申
- 扣车:未带2证2标1号牌
- 信息变更30日,有效期满90日
- 轻1中2大3
- 无逃3以下
- 先死后逃3-7
- 逃逸致死7以上
- 右让左、弯让直、右侧先
- 有障碍让无障碍,靠山让临崖
- 白色同向,黄色对向
- 实线不可跨,虚线可跨
- 前扇后方,圆直方单
- 一急二反三连续
- 英文缩写题
- 看见T(Traffic)就找交通 –> TMC:实时交通(Traffic Message Channel)
- 有T有S找识别 –> TSR:交通标志识别(Traffic Sign Recognition)
- 看见CC就找巡 –> ACC:自适应巡航(Adaptive Cruise Control)
- 多个S找定速 –> CCS:定速巡航(Cruise Control System)
- 偏离碰撞都会W(歪) –> LDW:车道偏离预警(Lane Departure Warning)
- 碰撞还容易F(翻车) –> FCW:前方碰撞预警(Forward Collision Warning)
- 制动刹车找EB –> EBD:电子制动分配(Electronic Brake Distribution)
- A在最前找自动 –> AEB:自动刹车辅助(Autonomous Emergency Braking)
- A在最后找辅助 –> EBA:紧急制动辅助(Emergency Brake Assist)
- 看见BS找盲点 –> BSD:盲点监测系统(Blind Spot Detection)
- A在最后找辅助 –> BSA:盲点辅助系统(Blind Spot Assist)
- SP身平,所以找稳定 –> ESP:车身稳定系统(Electronic Stability Program)
- 最长找牵引 –> TCS/ASR/TRC:牵引力控制(Traction Control System/Anti-Slip Regulation/Traction Control)
- 好感词:
- 减速、停车、靠右
- 让行、安全距离
- 观察、依次
- 警示词:
- 迅速、加速、快速
- 立即、随意、空挡滑行(山区×)
- 紧急制动、急转向
- 鸣喇叭:
- 连续、持续催促 ×
- 坡顶、急弯、提醒 √
- 选择题关键词:
- 看见臂章选左转弯
- 看见临时停车,找危险报警闪光灯
- 特殊速度找30
- 车相关找登记,人相关找核发
- 雾天找雾灯
- 交通肇事罪,选除了3个“未”以外的
- 行驶证、号牌找登记,驾驶证找核发
- 判断题选错的关键词
- 拼装报废车上路就选错(而且抓到是扣驾驶证,不是拘留)
- 无证驾驶(比如丢失、超过有效期、暂扣、吊销等)
- 实习期内单独上高速选错误
- 使用其他车“两证两标一号牌”选错
- 两证两标一号牌(缺少一项就要扣车)扣行驶证、身份证就选错
- 有精神病、癫痫、吸毒、服用精神药品开车就选错
- 看到2分直接选错
- 出现会保留相应学习记录直接选错(有违法行为,相应成绩无效选对)
- C1驾驶C6错误
- 县级以上医疗证直接选错
- 看到“只能”就选错
- “必须”一般是错误的(但是如果是必须减速、有伤亡必须报警是对的)
- 直接驶入、直接驶回、迅速驶入都是错的
- 允许熄火滑行、利用空挡滑行都是错的(只有利用发动机制动是对的)
- 超实线超车选错误
- 不避让、不让行都是错的(人车非机动车)
- 驾考里面不开灯一般是错的,而且要提前开(只有进入环岛不开灯)
- 看到开远光灯选错误
- 看到同时开启转向灯选错误
- 看到高速倒回出口,停车,逆行选错误
- 隧道超车选错误
- 借用、占用应急车道、公交车车道行驶、超车或停车选错误
- 驱赶催促选错误(人、车、动物)
- 重踏持续踏都是错的(间断轻踏就选对)
4.补充扣分罚款
- 校中危:校车、中型以上载货载客汽车、危险物品运输车辆
- 中危:中型以上载货载客汽车、危险物品运输车辆
-
普通车:校车、中型以上载货载客汽车、危险物品运输车辆以外的机动车
-
公旅危:公路客运汽车、旅游客运汽车、危险物品运输车辆
- 校公旅:校车、公路客运汽车、旅游客运汽车
- 七座客:7座以上载客汽车
-
普通客:校车、公路客运汽车、旅游客运汽车、7座以上载客汽车以外的其它载客车
- 高快路:高速公路、城市快速路
- 普通路:高速公路、城市快速路以外的道路
关键词 | 犯错类型 | 扣分 |
---|---|---|
车牌号 | 伪造、变造(包括行驶证、驾驶证、校车标牌),或使用其他车(行驶证) | 12分 |
未悬挂、故意遮挡、污损 | 9分 | |
不按规定安装,比如翘起 | 3分 | |
驾驶证 | 驾驶与准驾车型不符的机动车 | 9分 |
未取得校车驾驶资格驾驶校车 | 9分 | |
驾驶证被暂扣或者扣留期间驾驶启动车 | 6分 | |
未按时进行安全技术检验 | 校车,或存在安全隐患 | 3分 |
公旅危 | 3分 | |
公旅危以外的车 | 1分 | |
载运危险品、不可解体的物品 | 未按指定的时间、路线、速度行驶或未悬挂警示标志并有安全措施 | 6分 |
不可解体品,未按指定的时间、路线、速度行驶或未悬挂警示标志 | 6分 | |
未批准进入危险化学品运输车辆限制通行区域的 | 6分 | |
安全驾驶 | 饮酒驾驶(醉驾吊销驾驶证) | 12分 |
接打手持电话 | 3分 | |
不系安全带 | 1分 | |
摩托车不带头盔 | 1分 | |
疲劳驾驶 | 连续驾驶中危超过4小时未停车休息或者停车休息少于20分钟 | 9分 |
连续驾驶货车超过4小时未停车休息或者停车休息少于20分钟 | 6分 | |
车辆灯光、交通信号灯 | 不按交通信号灯指示通行(穿红灯) | 6分 |
在道路上车辆发生故障、事故停车后,不按规定使用灯光或设置警告标志 | 3分 | |
不按规定使用灯光 | 1分 | |
交通事故 | 造成致人轻伤以上或者死亡的交通事故后逃逸,尚不构成犯罪的 | 12分 |
造成致人轻微伤或者财产损失的交通事故后逃逸,尚不构成犯罪的 | 6分 | |
其它 | 代替实际机动车驾驶人接受交通违法行为处罚和记分牟取利益的 | 12分 |
违反禁令标志、禁止标线指示的 | 1分 | |
载货长度、宽度、高度超过规定 | 1分 | |
擅自改造的货车 | 1分 |
- 超速、超员、超载
使用C++调用Pytorch
简介:工作需要,需要使用C++来使用Pytorch,学无止境呀!
入门
在两眼一眼的情况下先问Kimi:首先,使用Pytorch进行模型训练最优的方式还是使用Python接口,简单易用,那么什么情况下需要用Pytorch的C++接口呢?—— 往往就是在模型部署推理阶段。其次,就看怎么实现模型C++部署:
- 先将Python环境中训练好的模型文件导出为TorchScript格式,其实就是“xxx.pt”文件;
- 安装LibTorch(PyTorch的C++分发版),使用C++版本的API函数加载模型文件和编写计算代码;
- 写CMakeLists.txt使用CMake来进行编译管理;
初次尝试
- 下载安装LibTorch ```bash wget https://download.pytorch.org/libtorch/cpu/libtorch-cxx11-abi-shared-with-deps-2.3.1%2Bcpu.zip
unzip libtorch-cxx11-abi-shared-with-deps-2.3.1%2Bcpu.zip
2. 编写一个简单的C++代码测试LibTorch
```C++
// example-app.cpp
#include <torch/torch.h>
#include <iostream>
int main() {
torch::Tensor tensor = torch::rand({2, 3});
std::cout << tensor << std::endl;
}
- 编写一个CMakeLists.txt ```CMake cmake_minimum_required(VERSION 3.0 FATAL_ERROR) project(example-app)
设置C++17标准
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON)
查找Torch包
find_package(Torch REQUIRED)
添加可执行文件
add_executable(example-app example-app.cpp)
链接Torch库
target_link_libraries(example-app “${TORCH_LIBRARIES}”)
确保C++17标准被正确传递
if(CMAKE_CXX_COMPILER_ID STREQUAL “GNU”) target_compile_options(example-app PRIVATE “-Wall” “-Wextra”) elseif(CMAKE_CXX_COMPILER_ID STREQUAL “Clang”) target_compile_options(example-app PRIVATE “-stdlib=libc++”) endif()
确保C++17标准被正确传递
set_target_properties(example-app PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON )
4. 编译运行
```bash
mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH=./libtorch ..
# 编译
make
# 运行
./example-app
另外,如果已经安装了Pytorch的Python库,那么不下载LibTorch也是可以编译运行的:
cmake -DCMAKE_PREFIX_PATH=`python3 -c 'import torch;print(torch.utils.cmake_prefix_path)'` ..
后知后觉,发现Pytorch官方LibTorch下载使用文档。
在ARM 64位(aarch64)设备上编译libtorch
在自己电脑跑通,但在需要运行的国产机上报错:
Scanning dependencies of target example-app
[ 50%] Building CXX object CMakeFiles/example-app.dir/example-app.cpp.o
[100%] Linking CXX executable example-app
/usr/bin/ld: ../libtorch/lib/libtorch.so: error adding symbols: file in wrong format
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/example-app.dir/build.make:88:example-app] 错误 1
make[1]: *** [CMakeFiles/Makefile2:76:CMakeFiles/example-app.dir/all] 错误 2
make: *** [Makefile:84:all] 错误 2
使用uname -m
才发现是ARM 64位(aarch64)的设备,看网上说需要源码编译。
先下载源码:
git clone https://github.com/pytorch/pytorch --recursive && cd pytorch
git checkout v1.2.0
git submodule sync
git submodule update --init --recursive
上面四条命令依次解释如下:
- –recursive选项确保了同时克隆了所有子模块(submodules),这些子模块是PyTorch项目依赖的其他Git仓库。
- 这条命令切换到PyTorch的v1.2.0版本。
- 这条命令用于与远程仓库同步子模块的引用。这确保了子模块的引用与远程仓库中的当前状态一致。
- 这条命令用于初始化和更新所有子模块。–init选项用于创建所有尚未克隆的子模块,–recursive选项用于递归地更新所有子模块。
简单设置:
export USE_CUDA=False
export BUILD_TEST=False
将USE_CUDA设置为False意味着在编译或运行程序时,将不会使用CUDA,也就是说,程序将不会利用GPU进行加速。设置BUILD_TEST为False时,编译系统可能会跳过构建测试代码的步骤,这可以加快编译过程,特别是在不需要测试代码的情况下。
编译:
mkdir build && cd build
python3 ../tools/build_libtorch.py
编译完成之后,整理成libtorch:
# libtorch/
mkdir include
cp -r /home/kylin/zw/pytorch/torch/ ./include
cp -r /home/kylin/zw/pytorch/caffe2/ ./include
cp -r /home/kylin/zw/pytorch/c10/ ./include
cp -r /home/kylin/zw/pytorch/aten/src/ATen/ ./include
cp -r /home/kylin/zw/pytorch/aten/src/TH/ ./include
cp -r /home/kylin/zw/pytorch/aten/src/THCUNN/ ./include
cp -r /home/kylin/zw/pytorch/aten/src/THNN/ ./include
cp -r /home/kylin/zw/pytorch/build/build/lib ./
mkdir share
cp -r /home/kylin/zw/pytorch/torch/share/cmake ./share
文件结构:
├── include
│ ├── ATen
│ ├── c10
│ ├── caffe2
│ ├── TH
│ ├── THCUNN
│ ├── THNN
│ └── torch
├── lib
└── share
└── cmake
折腾了一番之后还是报错:
kylin@kylin-190:~/zw/CppTorch/build$ make
Scanning dependencies of target example-app
[ 50%] Building CXX object CMakeFiles/example-app.dir/example-app.cpp.o
In file included from /home/kylin/zw/LibTorch/include/torch/csrc/WindowsTorchApiMacro.h:3,
from /home/kylin/zw/LibTorch/include/torch/csrc/api/include/torch/cuda.h:3,
from /home/kylin/zw/LibTorch/include/torch/csrc/api/include/torch/all.h:3,
from /home/kylin/zw/LibTorch/include/torch/csrc/api/include/torch/torch.h:3,
from /home/kylin/zw/CppTorch/example-app.cpp:1:
/home/kylin/zw/LibTorch/include/c10/macros/Export.h:44:10: fatal error: c10/macros/cmake_macros.h: 没有那个文件或目录
44 | #include "c10/macros/cmake_macros.h"
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
make[2]: *** [CMakeFiles/example-app.dir/build.make:63:CMakeFiles/example-app.dir/example-app.cpp.o] 错误 1
make[1]: *** [CMakeFiles/Makefile2:76:CMakeFiles/example-app.dir/all] 错误 2
make: *** [Makefile:84:all] 错误 2
《从头开始构建大语言模型》03
简介:这是《从头开始构建大语言模型》的第三章。
3 编程实现自注意力机制
本章涵盖:
- 探讨在神经网络中使用注意机制的原因
- 引入基本的自注意力框架,并逐步发展为增强的自注意力机制
- 实现一个因果注意力模块,允许LLMs每次生成一个token
- 用dropout屏蔽随机选择的注意力权重,以减少过拟合
- 将多个因果注意模块堆叠成一个多头注意模块
在前一章中,您学习了如何为训练LLMs准备输入文本。这涉及到将文本分割成单独的词和子词tokens,这些tokens可以编码为LLM的向量表示,即所谓的嵌入向量。
在本章中,我们将研究LLM架构本身的一个组成部分——注意机制,如图3.1所示。
图 3.1 对LLM进行编码、在一般文本数据集上对LLM进行预训练以及在标记数据集上对其进行微调的三个主要阶段的模型流程图。这一章的重点是自注意力机制,这是LLM架构的一个组成部分。
注意机制是一个复杂的主题,这就是为什么我们要用一整章来讨论它。我们将在很大程度上孤立地看待这些注意机制,并在机制层面上关注它们。在下一章中,我们将对LLM中自注意力机制的其余部分进行编程,以了解它的作用并创建一个模型来生成文本。
在本章的过程中,我们将实现注意机制的四种不同变体,如图3.2所示。
图 3.2 该图描述了我们将在本章中编码的不同注意机制,在添加可训练权重之前,从简化版本的自注意开始。因果注意机制为自注意添加了一个掩码,允许LLM一次生成一个单词。最后,多头注意将注意力机制组织成多个头部,允许模型并行捕获输入数据的各个方面。
图3.2所示的这些不同的注意力变体相互构建,目标是在本章结束时实现一个紧凑而有效的多头注意力实现,然后我们可以将其插入到LLM架构中,我们将在下一章中编程实现。
3.1 长序列建模的问题
在我们深入研究本章后面LLMs的核心——自注意机制之前,在LLMs之前没有注意机制的架构会有什么问题?假设我们想开发一个语言翻译模型,将文本从一种语言翻译成另一种语言。如图3.3所示,由于源语和目的语的语法结构不同,我们不能简单地逐字翻译文本。
图 3.3 在将文本从一种语言翻译成另一种语言时,例如将德语翻译成英语,不可能只是逐字翻译。相反,翻译过程需要上下文理解和语法对齐。
为了解决我们无法逐字翻译文本的问题,通常使用具有两个子模块的深度神经网络,即所谓的编码器和解码器。编码器的工作是首先读取并处理整个文本,然后解码器生成翻译后的文本。
当我们在第1章(第1.4节,为不同的任务使用LLMs)中介绍变压器架构时,我们已经简要地讨论了编码器-解码器网络。在变压器出现之前,递归神经网络(RNN)是语言翻译中最流行的编码器-解码器架构。
RNN是一种神经网络,其中前一步的输出作为当前步骤的输入,使它们非常适合于文本等顺序数据。如果你不熟悉RNN,不要担心,你不需要知道RNN的详细工作原理来参与这个讨论;我们在这里更多地关注编码器-解码器设置的一般概念。
在编码器-解码器RNN中,输入文本被送入编码器,编码器依次处理文本。编码器在每一步更新其隐藏状态(隐藏层的内部值),试图在最终隐藏状态下捕获输入句子的整个含义,如图3.4所示。然后,解码器利用这个最终的隐藏状态开始生成翻译后的句子,一次一个单词。它还在每一步更新其隐藏状态,该状态应该携带下一个单词预测所需的上下文。
图 3.4 在Transformer模型出现之前,编码器-解码器RNN是机器翻译的热门选择。编码器从源语言中获取一系列tokens作为输入,其中编码器的隐藏状态(中间神经网络层)编码整个输入序列的压缩表示。然后,解码器使用其当前的隐藏状态开始翻译,一个接一个的tokens。
我们不需要知道这些编码器-解码器RNN的内部工作原理,但这里的关键思想是编码器部分将整个输入文本处理为隐藏状态(存储单元)。然后,解码器接受这种隐藏状态来产生输出。你可以把这个隐藏状态想象成一个嵌入向量,这个概念我们在第二章讨论过。
编码器-解码器RNN的一个大问题和局限性是,在解码阶段,RNN不能直接访问编码器先前的隐藏状态。因此,它完全依赖于当前隐藏状态,它封闭了所有相关信息。这可能导致上下文的丢失,特别是在复杂的句子中,依赖关系可能跨越很长的距离。
对于不熟悉RNN的读者,没有必要理解或研究这种架构,因为我们将不会在本书中使用它。本节的要点是,编码器-解码器RNN有一个缺点,这激发了注意力机制的设计。
3.2 使用注意机制捕获数据依赖关系
在Transformer LLMs之前,如前所述,通常使用RNN来完成语言建模任务,如语言翻译。RNN可以很好地翻译短句,但对于较长的文本就不太适用了,因为它们不能直接访问输入中的前一个单词。
这种方法的一个主要缺点是,RNN必须在将其传递给解码器之前以单一隐藏状态记住整个编码输入,如前一节中的图3.4所示。
因此,研究人员在2014年开发了RNN的Bahdanau注意机制(以相应论文的第一作者命名),该机制修改了编码器-解码器RNN,使解码器可以在每个解码步骤中选择性地访问输入序列的不同部分,如图3.5所示。
图 3.5 使用注意机制,网络的文本生成解码器部分可以选择性地访问所有输入tokens。这意味着对于生成给定的输出tokens,一些输入tokens比其他输入token更重要。重要性是由所谓的注意力权重决定的,我们将在后面计算。请注意,该图显示了注意力背后的一般思想,并没有描述Bahdanau机制的确切实现,这是本书范围之外的RNN方法。
有趣的是,仅仅三年后,研究人员发现构建用于自然语言处理的深度神经网络并不需要RNN架构,并提出了原始的transformer架构(在第1章中讨论),该架构具有受Bahdanau注意机制启发的自注意机制。
自注意力是一种机制,它允许输入序列中的每个位置在计算序列表示时关注同一序列中的所有位置。自注意力机制是基于Transformer体系结构的当代LLMs(如GPT系列)的一个关键组成部分。
本章的重点是编码和理解GPT类模型中使用的这种自注意机制,如图3.6所示。在下一章中,我们将对LLM的其余部分进行编程。
图 3.6 自注意力是Transformer中的一种机制,通过允许序列中的每个位置与同一序列中所有其他位置相互作用并权衡其重要性,用于计算更有效的输入表示。在本章中,我们将从头开始编写这种自我注意机制,然后在下一章中编写类似GPT的LLM的其余部分。
3.3 使用自注意力关注输入的不同部分
现在,我们将深入研究自我注意力机制的内部工作原理,并学习如何从头开始编写它。自注意力是每个基于Transformer体系结构的LLM的基石。
值得注意的是,这个主题可能需要大量的关注和注意(没有双关语的意思),但是一旦你掌握了它的基本原理,你就征服了本书中最难的一个方面,并在总体上实现了LLMs。
自注意力中的“自”
在自注意中,“自”指的是该机制通过关联同个输入序列中的不同位置来计算注意权重的能力。它评估和学习输入本身的各个部分之间的关系和依赖关系,例如句子中的单词或图像中的像素。这与传统的注意力机制形成了对比,传统的注意力机制关注的是两个不同序列元素之间的关系,比如在序列到序列模型中,注意力可能在输入序列和输出序列之间比较,如图3.5所示。
因为自我注意看起来很复杂,特别是当你第一次遇到它的时候,我们将在下一小节开始介绍一个简化版本的自我注意。之后,在3.4节中,我们将实现在LLMs中使用的具有可训练权值的自注意力机制。
3.3.1 没有可训练权重的简单自注意机制
在本节中,我们实现了一种简化的自注意力变体,没有任何可训练的权重,如图3.7所示。本节的目的是在3.4节添加可训练权重之前,说明自我注意力中的几个关键概念。
图 3.7 自注意的目标是为每个输入元素计算一个上下文向量,它结合了来自所有其他输入元素的信息。在图中描述的示例中,我们计算上下文向量$z^{(2)}$。计算$z^{(2)}$的每个输入元素的重要性或贡献由注意力权重$α_{21}$到$α_{2T}$决定。在计算$z^{(2)}$时,注意力权重是根据输入元素$x^{(2)}$和所有其他输入来计算的。这些注意力权重的精确计算将在本节后面讨论
图3.7给出了一个输入序列,用$x$表示,由$T$个元素组成,用$x^{(1)}$到$x^{(T)}$表示。这个序列通常表示文本,比如一个句子,它已经被转换成tokens嵌入,如第2章所述。
例如,考虑这样一个输入文本:“Your journey starts with one step.”,在这种情况下,序列的每个元素,例如$x^{(1)}$,对应于表示特定标记(如“Your”)的$d$维嵌入向量。在图3.7中,这些输入向量显示为三维嵌入。
在自注意力中,我们的目标是计算输入序列中每个元素$x^{(i)}$的上下文向量$z^{(i)}$。上下文向量可以解释为一个丰富的嵌入向量。
为了说明这个概念,让我们把重点放在第二个输入元素的嵌入向量$x^{(2)}$(对应于标记“journey”)和相应的上下文向量$z^{(2)}$上,如图3.7底部所示。这个增强的上下文向量$z^{(2)}$是一个包含关于$x^{(2)}$和所有其他输入元素$x^{(1)}$到$x^{(T)}$的信息的嵌入。
在自注意力中,语境向量起着至关重要的作用。它们的目的是通过合并序列中所有其他元素的信息来创建输入序列(如句子)中每个元素的丰富表示,如图3.7所示。这在LLMs中是必不可少的,因为LLMs需要理解句子中单词之间的关系和相关性。稍后,我们将添加可训练的权重,帮助LLM学习构建这些上下文向量,以便它们生成与LLM相关的下一个token。
在本节中,我们实现了一个简化的自注意力机制来一步一步地计算这些权重和结果上下文向量。
考虑下面的输入句子,它已经被嵌入到第2章讨论的三维向量中。我们选择了一个小的嵌入尺寸,以确保它适合没有换行的页面:
import torch
inputs = torch.tensor(
[[0.43, 0.15, 0.89], # Your (x^1)
[0.55, 0.87, 0.66], # journey (x^2)
[0.57, 0.85, 0.64], # starts (x^3)
[0.22, 0.58, 0.33], # with (x^4)
[0.77, 0.25, 0.10], # one (x^5)
[0.05, 0.80, 0.55]] # step (x^6)
)
实现自我注意的第一步是计算中间值$ω$,称为注意力得分,如图3.8所示。
图 3.8 本节的总体目标是说明使用第二个输入序列$x^{(2)}$作为query来计算上下文向量$z^{(2)}$。该图显示了第一个中间步骤,通过点积计算query $x^{(2)}$与所有其他输入元素之间的注意力分数$ω$。(请注意,图中的数字在小数点后被截断为一位,以避免混乱。)
图3.8说明了我们如何计算query token和每个输入token之间的中间注意力分数。我们通过计算query $x^{(2)}$与每个其他输入token的点积来确定这些分数:
query = inputs[1]
attn_scores_2 = torch.empty(inputs.shape[0])
for i, x_i in enumerate(inputs):
attn_scores_2[i] = torch.dot(x_i, query)
print(attn_scores_2)
计算出的注意分数如下:
tensor([0.9544, 1.4950, 1.4754, 0.8434, 0.7070, 1.0865])
理解点积
点积本质上是一种简洁的计算方法,将两个向量的逐元素相乘,然后将乘积相加,我们可以这样演示:
res = 0. for idx, element in enumerate(inputs[0]): res += inputs[0][idx] * query[idx] print(res) print(torch.dot(inputs[0], query))
输出证实了逐元素乘法的和与点积的结果相同:
tensor(0.9544) tensor(0.9544)
除了将点积运算视为一种将两个向量组合起来产生标量值的数学工具之外,点积还是一种相似性度量,因为它量化了两个向量对齐的程度:点积越高表示向量之间对齐或相似的程度越高。在自注意力机制的背景下,点积决定了序列中元素相互关注的程度:点积越高,两个元素之间的相似性和注意力得分越高。
在下一步中,如图3.9所示,我们将之前计算的每个注意力分数归一化。
图 3.9 对输入query $x^{(2)}$计算出注意分数$ω_{21} \sim ω_{2T}$后,通过对注意分数进行归一化,得到注意力权重$α_{21} \sim α_{2T}$。
图3.9所示的归一化背后的主要目标是获得总计为1的注意力权重。这种规范化是一种惯例,对解释和维护LLM中的训练稳定性很有用。这里有一个实现这个规范化步骤的简单方法:
attn_weights_2_tmp = attn_scores_2 / attn_scores_2.sum()
print("Attention weights:", attn_weights_2_tmp)
print("Sum:", attn_weights_2_tmp.sum())
如输出所示,注意力权重现在之和为1:
Attention weights: tensor([0.1455, 0.2278, 0.2249, 0.1285, 0.1077, 0.1656])
Sum: tensor(1.0000)
在实践中,使用softmax函数进行规范化更为常见和可取。这种方法可以更好地管理极值,并在训练过程中提供更有利的梯度属性。下面是softmax函数的基本实现,用于规范化注意力分数:
def softmax_naive(x):
return torch.exp(x) / torch.exp(x).sum(dim=0)
attn_weights_2_naive = softmax_naive(attn_scores_2)
print("Attention weights:", attn_weights_2_naive)
print("Sum:", attn_weights_2_naive.sum())
如输出所示,softmax函数也满足目标,并将注意力权重归一化,使其总和为1:
Attention weights: tensor([0.1385, 0.2379, 0.2333, 0.1240, 0.1082, 0.1581])
Sum: tensor(1.)
此外,softmax功能确保注意力权重始终为正。这使得输出可以解释为概率或相对重要性,其中权重越大表示重要性越大。
注意,这个简单的softmax实现(softmax_naive)在处理大或小的输入值时可能会遇到数值不稳定问题,例如溢出和下溢。因此,在实践中,建议使用softmax的PyTorch实现,该实现已针对性能进行了广泛优化:
attn_weights_2 = torch.softmax(attn_scores_2, dim=0)
print("Attention weights:", attn_weights_2)
print("Sum:", attn_weights_2.sum())
在这种情况下,我们可以看到它产生的结果与之前的softmax_naive函数相同:
Attention weights: tensor([0.1385, 0.2379, 0.2333, 0.1240, 0.1082, 0.1581])
Sum: tensor(1.)
现在我们计算了标准化的注意力权重,我们准备好了图3.10所示的最后一步:通过将嵌入的输入token $x^{(i)}$与相应的注意力权重相乘来计算上下文向量$z^{(2)}$,然后将结果向量相加。
图 3.10 在计算并归一化注意力分数得到query $x^{(2)}$的注意权重后,最后一步是计算上下文向量$z^{(2)}$。这个上下文向量是所有输入向量$x^{(1)}$到$x^{(T)}$的组合,由注意力权重加权得到。
图3.10所示的上下文向量$z^{(2)}$计算为所有输入向量的加权和。这包括将每个输入向量乘以其相应的注意力权重:
query = inputs[1] # 2nd input token is the query
context_vec_2 = torch.zeros(query.shape)
for i,x_i in enumerate(inputs):
context_vec_2 += attn_weights_2[i]*x_i
print(context_vec_2)
计算结果如下:
tensor([0.4419, 0.6515, 0.5683])
在下一节中,我们将推广计算上下文向量的这个过程,以同时计算所有上下文向量。
3.3.2 计算所有输入tokens的注意力权重
在上一节中,我们计算了输入2的注意力权重和上下文向量,如图3.11中突出显示的行所示。现在,我们将这个计算扩展到计算所有输入的注意力权重和上下文向量。
图 3.11 突出显示的行显示了第二个输入元素作为查询的注意力权重,正如我们在前一节中计算的那样。本节概括计算以获得所有其他注意力权重。
我们遵循与前面相同的三个步骤,如图3.12所示,只是在代码中做了一些修改,以计算所有上下文向量,而不是只计算第二个上下文向量$z^{(2)}$。
图 3.12 在自注意力中,我们首先计算注意力分数,然后对其进行归一化以获得总和为 1 的注意力权重。这些注意力权重用于将上下文向量计算为输入的加权和。
首先,在图3.12所示的步骤1中,我们添加了一个额外的for循环来计算所有输入对的点积:
attn_scores = torch.empty(6, 6)
for i, x_i in enumerate(inputs):
for j, x_j in enumerate(inputs):
attn_scores[i, j] = torch.dot(x_i, x_j)
print(attn_scores)
得到的注意力得分如下:
tensor([[0.9995, 0.9544, 0.9422, 0.4753, 0.4576, 0.6310],
[0.9544, 1.4950, 1.4754, 0.8434, 0.7070, 1.0865],
[0.9422, 1.4754, 1.4570, 0.8296, 0.7154, 1.0605],
[0.4753, 0.8434, 0.8296, 0.4937, 0.3474, 0.6565],
[0.4576, 0.7070, 0.7154, 0.3474, 0.6654, 0.2935],
[0.6310, 1.0865, 1.0605, 0.6565, 0.2935, 0.9450]])
前面张量中的每个元素表示每对输入之间的注意得分,如图3.11所示。注意,图3.11中的值是归一化的,这就是为什么它们不同于前面张量中未归一化的注意力得分。稍后我们会讨论归一化。
在计算前面的注意力分数张量时,我们在Python中使用了for循环。然而,for循环通常很慢,我们可以使用矩阵乘法获得相同的结果:
attn_scores = inputs @ inputs.T
print(attn_scores)
我们可以直观地确认结果与之前相同:
tensor([[0.9995, 0.9544, 0.9422, 0.4753, 0.4576, 0.6310],
[0.9544, 1.4950, 1.4754, 0.8434, 0.7070, 1.0865],
[0.9422, 1.4754, 1.4570, 0.8296, 0.7154, 1.0605],
[0.4753, 0.8434, 0.8296, 0.4937, 0.3474, 0.6565],
[0.4576, 0.7070, 0.7154, 0.3474, 0.6654, 0.2935],
[0.6310, 1.0865, 1.0605, 0.6565, 0.2935, 0.9450]])
在步骤2中,如图3.12所示,我们现在将每行归一化,使每行中的值之和为1:
attn_weights = torch.softmax(attn_scores, dim=-1)
print(attn_weights)
这将返回以下与图3.10所示值匹配的注意权重张量:
tensor([[0.2098, 0.2006, 0.1981, 0.1242, 0.1220, 0.1452],
[0.1385, 0.2379, 0.2333, 0.1240, 0.1082, 0.1581],
[0.1390, 0.2369, 0.2326, 0.1242, 0.1108, 0.1565],
[0.1435, 0.2074, 0.2046, 0.1462, 0.1263, 0.1720],
[0.1526, 0.1958, 0.1975, 0.1367, 0.1879, 0.1295],
[0.1385, 0.2184, 0.2128, 0.1420, 0.0988, 0.1896]])
在我们进入步骤3(图3.12所示的最后一步)之前,让我们简单地验证一下这些行确实都和为1:
row_2_sum = sum([0.1385, 0.2379, 0.2333, 0.1240, 0.1082, 0.1581])
print("Row 2 sum:", row_2_sum)
print("All row sums:", attn_weights.sum(dim=-1))
结果如下:
Row 2 sum: 1.0
All row sums: tensor([1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000])
在第三步,也是最后一步,我们现在使用这些注意力权重通过矩阵乘法来计算所有上下文向量:
all_context_vecs = attn_weights @ inputs
print(all_context_vecs)
在结果输出张量中,每行包含一个三维上下文向量:
tensor([[0.4421, 0.5931, 0.5790],
[0.4419, 0.6515, 0.5683],
[0.4431, 0.6496, 0.5671],
[0.4304, 0.6298, 0.5510],
[0.4671, 0.5910, 0.5266],
[0.4177, 0.6503, 0.5645]])
通过将第二行与之前在3.3.1节中计算的上下文向量$z^{(2)}$进行比较,我们可以再次检查代码是否正确:
print("Previous 2nd context vector:", context_vec_2)
从结果可以看出,前面计算的context_vec_2与前面张量的第二行完全匹配:
Previous 2nd context vector: tensor([0.4419, 0.6515, 0.5683])
至此结束了简单的自关注机制的代码演练。在下一节中,我们将添加可训练的权重,使LLM能够从数据中学习并提高其在特定任务上的性能。
3.4 实现带可训练权重的自注意力
在本节中,我们将实现在原始Transformer体系结构、GPT模型和大多数其他流行的LLMs中使用的自注意力机制。这种自注意机制也被称为尺度点积注意力。图3.13提供了一个理想模型,说明这种自注意机制如何匹配实现LLM中的更广泛的上下文内容。
图 3.13 该图是我们在本节编程的自注意机制如何适应本书和本章的更广泛文本内容的理想模型。在前一节中,我们编写了一个简化的注意机制来理解注意机制背后的基本机制。在本节中,我们将为这种注意机制添加可训练的权重。在接下来的部分中,我们将通过添加因果掩码和多个heads来扩展这种自注意机制。
如图3.13所示,具有可训练权重的自关注机制建立在前面的概念之上:我们想要计算上下文向量,作为特定于某个输入元素的输入向量的加权和。正如您将看到的,与我们之前在3.3节中编写的基本自注意机制相比,只有细微的区别。
最显著的区别是引入了在模型训练期间更新的权重矩阵。这些可训练的权重矩阵是至关重要的,这样模型(特别是模型中的注意力模块)就可以学会产生“好的”上下文向量。(注意,我们将在第5章训练LLMs。)
我们将在两个小节中讨论这种自我关注机制。首先,我们将像以前一样一步一步地编写代码。其次,我们将把代码组织到一个紧凑的Python类中,这个类可以导入到LLM架构中,我们将在第4章中对其进行编程。
3.4.1 逐步计算注意力权重
我们将通过引入三个可训练的权重矩阵$W_q$、$W_k$和$W_v$来逐步实现自注意力机制。这三个矩阵用于将嵌入的输入token $x^{(i)}$投影到查询、键和值向量中,如图3.14所示。
图 3.14 在具有可训练权矩阵的自注意力机制的第一步,我们计算输入元素$x$的查询($q$)、键($k$)和值($v$)向量。与前几节类似,我们将第二个输入$x^{(2)}$指定为查询输入。查询向量$q^{(2)}$是通过输入$x^{(2)}$和权重矩阵$W_q$之间的矩阵乘法得到的。同样,我们通过涉及权重矩阵$W_k$和$W_v$的矩阵乘法来获得键向量和值向量。
在前面的3.3.1节中,当我们计算简化的注意力权重以计算上下文向量$z^{(2)}$时,我们将第二个输入元素$x^{(2)}$定义为查询。之后,在3.3.2节中,我们将其推广到计算所有上下文向量$z^{(1)} \sim z^{(T)}$代表六个单词的输入句子“Your journey starts with one step.”。
同样,为了说明目的,我们将从只计算一个上下文向量$z^{(2)}$开始。在下一节中,我们将修改此代码以计算所有上下文向量。
让我们从定义几个变量开始:
x_2 = inputs[1]
d_in = inputs.shape[1]
d_out = 2
注意,在类似GPT的模型中,输入和输出维度通常是相同的,但是为了便于说明,为了更好地跟踪计算,我们在这里选择不同的输入(d_in=3)和输出(d_out=2)维度。
接下来,我们初始化三个权重矩阵$W_q$, $W_k$和$W_v$,如图3.14所示:
torch.manual_seed(123)
W_query = torch.nn.Parameter(torch.rand(d_in, d_out), requires_grad=False)
W_key = torch.nn.Parameter(torch.rand(d_in, d_out), requires_grad=False)
W_value = torch.nn.Parameter(torch.rand(d_in, d_out), requires_grad=False)
请注意,我们设置requires_grad=False是为了减少输出中的混乱,但如果我们要使用权重矩阵进行模型训练,我们将设置requires_grad=True以在模型训练期间更新这些矩阵。
接下来,我们计算查询、键和值向量,如图3.14所示:
query_2 = x_2 @ W_query
key_2 = x_2 @ W_key
value_2 = x_2 @ W_value
print(query_2)
根据查询的输出,我们可以看到,由于我们通过d_out将相应权重矩阵的列数设置为2,因此结果是一个二维向量:
tensor([0.4306, 1.4551])
权重参数vs注意力权重
注意,在权重矩阵$W$中,术语“权重”是“权重参数”的缩写,即在训练过程中优化的神经网络的值。不要将这与注意力权重混淆。正如我们在上一节已经看到的,注意力权重决定了上下文向量对输入的不同部分的依赖程度,即网络对输入的不同部分的关注程度。 总而言之,权重参数是定义网络连接的基本学习系数,而注意力权重是动态的,特定于上下文的值。
尽管我们的临时目标是只计算一个上下文向量$z^{(2)}$,但我们仍然需要所有输入元素的键和值向量,因为它们涉及计算相对于查询$q^{(2)}$的注意力权重,如图3.14所示。
我们可以通过矩阵乘法得到所有的键和值:
keys = inputs @ W_key
values = inputs @ W_value
print("keys.shape:", keys.shape)
print("values.shape:", values.shape)
从输出中我们可以看出,我们成功地将6个输入标记从3D投影到2D嵌入空间:
keys.shape: torch.Size([6, 2])
values.shape: torch.Size([6, 2])
第二步是计算注意力得分,如图3.15所示。
图 3.15 注意分数计算是一个点积计算,类似于我们在3.3节简化的自注意机制中使用的计算。这里的新方面是,我们没有直接计算输入元素之间的点积,而是使用通过各自的权重矩阵转换输入获得的查询和键。
首先,让我们计算注意力得分$ω_{22}$:
keys_2 = keys[1]
attn_score_22 = query_2.dot(keys_2)
print(attn_score_22)
以下是未归一化注意力得分的结果:
tensor(1.8524)
同样,我们可以通过矩阵乘法将这个计算推广到所有注意力分数:
attn_scores_2 = query_2 @ keys.T # All attention scores for given query
print(attn_scores_2)
我们可以看到,作为快速检查,输出中的第二个元素与我们之前计算的attn_score_22匹配:
tensor([1.2705, 1.8524, 1.8111, 1.0795, 0.5577, 1.5440])
第三步是从注意力得分到注意力权重,如图3.16所示。
图 3.16 计算完注意力分数$ω$后,下一步就是使用softmax函数对这些分数进行归一化,得到注意力权重$α$。
接下来,如图3.16所示,我们通过缩放注意力分数和使用我们之前使用的softmax函数来计算注意力权重。与之前的不同之处在于,我们现在通过除以键的嵌入维度的平方根来衡量注意力得分(注意,取平方根在数学上与取0.5的幂是一样的):
d_k = keys.shape[-1]
attn_weights_2 = torch.softmax(attn_scores_2 / d_k**0.5, dim=-1)
print(attn_weights_2)
得到的注意权重如下:
tensor([0.1500, 0.2264, 0.2199, 0.1311, 0.0906, 0.1820])
缩放点积注意力背后的原理
采用嵌入维数归一化的原因是为了通过避免小梯度来提高训练性能。例如,当缩放嵌入维数时,对于类似GPT的LLMs,嵌入维数通常大于1000,由于应用了softmax函数,大的点积在反向传播期间可能导致非常小的梯度。随着点积的增加,softmax函数的行为更像阶跃函数,导致梯度接近于零。这些小的梯度会大大减慢学习速度或导致训练停滞。
这种自注意机制也被称为尺度点积注意力,原因是嵌入维数是平方根的尺度缩放。
现在,最后一步是计算上下文向量,如图3.17所示。
图 3.17 在自注意计算的最后一步,我们通过注意权值将所有值向量组合计算上下文向量。
与3.3节中计算上下文向量为输入向量的加权和类似,现在计算上下文向量为值向量的加权和。在这里,注意力权重作为加权因子,对每个值向量的各自重要性进行加权。与3.3节类似,我们可以使用矩阵乘法一步得到输出:
context_vec_2 = attn_weights_2 @ values
print(context_vec_2)
结果向量的内容如下:
tensor([0.3061, 0.8210])
到目前为止,我们只计算了一个上下文向量$z^{(2)}$。在下一节中,我们将泛化代码以计算输入序列$z^{(1)}$到$z^{(T)}$中的所有上下文向量。
为什么要查询、键和值?
注意机制上下文中的术语“键”、“查询”和“值”来自信息检索和数据库领域,其中使用了类似的概念来存储、搜索和检索信息。
“查询”类似于数据库中的搜索查询。它表示模型关注或试图理解的当前条目(例如,句子中的单词或tokens)。该查询用于探测输入序列的其他部分,以确定对它们的相关程度。
键”类似于用于索引和搜索的数据库键。在注意机制中,输入序列中的每个条目(例如,句子中的每个单词)都有一个关联的键。这些键用于匹配查询。
此上下文中的“值”类似于数据库中的键值对中的值。它表示输入项的实际内容或表示。一旦模型确定了哪些键(以及输入的哪些部分)与查询(当前关注项)最相关,它就会检索相应的值。
3.4.2 实现一个完整的自注意力Python类
在前面的小节中,我们已经完成了计算自注意输出的许多步骤。这主要是为了说明目的,这样我们就可以一步一步地进行。在实践中,考虑到下一章的LLM实现,将这些代码组织成一个Python类是有帮助的,如下所示:
**清单3.1 一个紧凑的自注意力类**
import torch.nn as nn
class SelfAttention_v1(nn.Module):
def __init__(self, d_in, d_out):
super().__init__()
self.d_out = d_out
self.W_query = nn.Parameter(torch.rand(d_in, d_out))
self.W_key = nn.Parameter(torch.rand(d_in, d_out))
self.W_value = nn.Parameter(torch.rand(d_in, d_out))
def forward(self, x):
keys = x @ self.W_key
queries = x @ self.W_query
values = x @ self.W_value
attn_scores = queries @ keys.T # omega
attn_weights = torch.softmax(
attn_scores / keys.shape[-1]**0.5, dim=-1)
context_vec = attn_weights @ values
return context_vec
在这段PyTorch代码中,SelfAttention_v1是一个派生自nn.Module的类,它是PyTorch模型的基本构建块,为模型层的创建和管理提供了必要的功能。
__init__方法为查询、键和值初始化可训练的权重矩阵(W_query、W_key和W_value),每个矩阵将输入维度d_in转换为输出维度d_out。
在前向传播过程中,使用forward函数,我们通过将查询和键相乘来计算注意力分数(attn_scores),并使用softmax对这些分数进行规范化。最后,我们用这些标准化的注意力得分来加权这些值,从而创建一个上下文向量。
我们可以这样使用这个类:
torch.manual_seed(123)
sa_v1 = SelfAttention_v1(d_in, d_out)
print(sa_v1(inputs))
由于输入包含六个嵌入向量,这就产生了一个存储六个上下文向量的矩阵:
tensor([[0.2996, 0.8053],
[0.3061, 0.8210],
[0.3058, 0.8203],
[0.2948, 0.7939],
[0.2927, 0.7891],
[0.2990, 0.8040]], grad_fn=<MmBackward0>)
作为快速检查,请注意第二行([0.3061,0.8210]
)是否匹配上一节中context_vec_2的内容。
图3.18总结了我们刚刚实现的自注意力机制。
图 3.18 在自注意中,我们将输入矩阵$X$中的输入向量用三个权矩阵$W_q$、$W_k$、$W_v$进行变换。然后,我们根据结果查询(Q)和键值(K)计算注意力权重矩阵。使用注意力权重和值(V),我们然后计算上下文向量(Z)。(为了视觉上表达的清晰,我们在这个图中关注单个输入文本的n个tokens,而不是一批多个输入。因此,在这种情况下,3D输入张量被简化为2D矩阵。这种方法允许对所涉及的过程进行更直接的可视化和理解。)
如图3.18所示,自注意涉及可训练权矩阵$W_q$、$W_k$、$W_v$。这些矩阵将输入数据转换为查询、键和值,这是注意力机制的关键组成部分。随着模型在训练过程中接触到更多的数据,它会调整这些可训练的权重,我们将在接下来的章节中看到。
我们可以利用PyTorch的nn.Linear来进一步改进SelfAttention_v1的实现。当bias单元被禁用时,其可以有效地执行矩阵乘法。此外,使用nn.Linear而不是手动实现nn.parameter (torch.rand(…))的一个显著优势是nn.Linear具有优化的权值初始化方案,有助于更稳定有效的模型训练。
**清单3.2 使用PyTorch的Linear层的自关注意力类**
class SelfAttention_v2(nn.Module):
def __init__(self, d_in, d_out, qkv_bias=False):
super().__init__()
self.d_out = d_out
self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_key = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_value = nn.Linear(d_in, d_out, bias=qkv_bias)
def forward(self, x):
keys = self.W_key(x)
queries = self.W_query(x)
values = self.W_value(x)
attn_scores = queries @ keys.T
attn_weights = torch.softmax(attn_scores / keys.shape[-1]**0.5, dim=-1)
context_vec = attn_weights @ values
return context_vec
你可以使用SelfAttention_v2类似于SelfAttention_v1:
torch.manual_seed(789)
sa_v2 = SelfAttention_v2(d_in, d_out)
print(sa_v2(inputs))
输出为:
tensor([[-0.0739, 0.0713],
[-0.0748, 0.0703],
[-0.0749, 0.0702],
[-0.0760, 0.0685],
[-0.0763, 0.0679],
[-0.0754, 0.0693]], grad_fn=<MmBackward0>)
注意,SelfAttention_v1和SelfAttention_v2给出不同的输出,因为它们对权重矩阵使用不同的初始权重,nn.Linear使用更复杂的权重初始化方案。
练习3.1比较SelfAttention_v1和SelfAttention_v2
3.5 通过因果注意力隐藏未来的单词
3.5.1 使用因果注意力掩码
3.5.2 实现一个完整的因果注意力类
3.6 将单头注意力扩展到多头注意力
3.6.1 堆叠多个单头注意力层
3.6.2 使用权重分离来实现多有注意力
3.7 小结
制作用于演示的视频/动图
简介:我常常在B站上看到UP主们将他们在桌面的操作制作成精美的视频,有时候还能局部放大以突出展示某一操作,我不知道怎么做到的,但我很想学会。所以,这值得探索一番。
ScreenToGif
我询问Kimi之后,打算最先尝试ScreenToGif。