Jump to content

皮条客

Moderators
  • Posts

    546
  • Joined

  • Last visited

About 皮条客

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

皮条客's Achievements

Mentor

Mentor (12/14)

  • The activity lasted for 1 week
  • Conversation Starter

Recent Badges

0

Reputation

  1. 标签:之声标题:世界时间多线程反应声控 前言 今天的"受害者"为【猫耳【调频】,一个音频网站 地址:https://www .埃文小姐。com/sound/m/110 对于本篇文章有疑问的同学可以加【资料白嫖、解答交流群:1039649593】 知识点: 要求 时间 是 并发期货 开发环境: 版本:anaconda5.2.0(python3.6.5) 编辑器:pycharm 【付费VIP完整版】只要看了就能学会的教程,80集Python基础入门视频教学 导入模块 导入时间 导入请求 进口并行。期货 进口是 通过函数式编程,实现各个功能模块 发送请求 def get_html(url): response=requests.get(url) 返回响应 第一次解析 极好的解析(响应): MP3 _ ids=re。全部查找(' a target=' _ player ' href='/sound/(.*?)”标题=.*?response.text) 返回mp3 _ ids 第二次解析 def parse_2(响应): json_data=response.json() title=JSON _ data[' info '][' sound '][' sound str '] 声音URL=JSON _ data[' info '][' sound '][' sound URL '] 返回标题,soundurl 保存数据 def保存(标题,mp3_data): 打开时(“mp3\\”标题“. mp3”,模式='wb ')为f: f.write(mp3_data) 打印(标题,下载完成!) 修改标题 def change_title(标题): new_title=re.sub(r'[\//|:*]',' _ ',标题) 返回新标题 主函数,调用里面包含的整体连贯 # 1.发送请求 响应=get_html(url) # 2.解析数据soundid mp3 _ ids=解析(响应) 对于mp3 _ ids:中的mp3_id # 3.请求另外详情页地址拼接https://www.missevan.com/sound/getsound?soundid=3922170 MP3 _ URL='https://www.missevan.com/sound/getsound?soundid=' mp3_id resp_2=get_html(mp3_url) # 4.解析音频全球资源定位器(统一资源定位符)地址音频标题 title,soundurl=parse_2(resp_2) # 修改标题 title=change_title(标题) # 5.请求音频全球资源定位器(统一资源定位符)地址音频二进制数据内容 mp3_data=get_html(soundurl).内容 # 6.下载保存到本地 保存(标题,mp3_data) 翻页 start_time=time.time() 对于范围(1,5):内的页面 打印(f )-正在爬取第{page}页- ') run(f ' https://www .埃文小姐。com/sound/m?id=110p={page} ') 打印('一共花费了' : ',time.time()-start_time) 多线程 if __name__=='__main__': start_time=time.time() 并发。未来。threadpoolexecutor(max _ workers=1000)作为执行者: 对于范围(1,5):内的页面 URL=f ' https://www .埃文小姐。com/sound/m?id=110p={page} ' executor.submit(运行,url) 打印('一共花费了' : ',time.time()-start_time) 速度提升了一分钟左右 原文链接:https://www.icode9.com/content-1-1151412.html
  2. 标签:www . cn网站ZYIskpan来源壁纸。 简介: 易哲壁纸网站源代码,使用360壁纸界面。 源代码下载地址https://www.skpan.cn/USnbQ5yTpVn. 图片: 原文链接:https://www.icode9.com/content-1-1151417.html
  3. 标签:产业新闻创新游戏编译TcaplusDB动画2021产业。 TcaplusDB一直密切关注游戏行业和数据库行业的动态。以下是TcaplusDB收集的游戏行业和数据库行业的最新消息,整理整理,奉献给大家观看。(本文部分内容来自互联网) 新场景、新模式、新业态 2021动漫游戏产业发展国际论坛开幕在即 2021动漫游戏产业发展国际论坛(以下简称“动漫论坛”)将于9月7日在北京林超松原大酒店举行。本次论坛以“动漫游戏新场景、新模式、新业态”为主题,分为动漫游戏产业发展国际论坛、——动漫游戏产业研讨会、IP跨境合作展三大板块。 据了解,动漫游戏产业发展国际论坛自成立以来,紧跟发展现状,解决行业痛点,探讨前沿趋势,已成为动漫游戏领域产业交流、产品介绍、技术展示、信息交流、粉丝互动、国际合作的重要品牌活动。 2021北京国际游戏创新大会将于9月下旬举办 BIGC 2021北京国际游戏创新大会新闻发布会昨天在北京举行。大会以“创新、引领、融合”为主题,将于今年9月24日至26日在海淀区召开。 会议分为两个部分:主要活动和辅助活动。主要活动由开幕式暨游戏创新峰会、主题分享、闭门座谈会、年度游戏推广、北京国际游戏创新展五大部分组成。《2021中国游戏创新及发展趋势报告》将在开幕式暨游戏创新峰会上权威发布。配套活动由理论研究论坛、主题交流活动、GAME BANG 2021北京国际游戏创新大会创作大赛、创新节四项活动组成。 首届全球游戏行业奖结果公开 《哈迪斯》 收获颇丰 上个月,国际游戏开发商协会(IGDA)宣布设立全球游戏产业奖(GIGA),其中包括40个奖项,以表彰在过去一年中为游戏产业提供激励的游戏和制造商。近日,奖项结果全部公开,右上角的动作游戏《哈迪斯》以10项提名、9项大奖(包括最重要的奖项“最佳游戏表现”)成为今年全球游戏产业奖的最终得主。 欧洲游戏行业去年收入233亿欧元,手游占比40% 近日,欧洲互动软件协会(ISFE)和欧洲游戏开发者联盟(EGDF)联合发布了一份新报告,总结了2020年欧洲游戏行业的情况。 报告显示,2020年欧洲游戏产业总收入达233亿欧元,同比增长22%,远高于2019年营收较2018年的增幅。在23亿欧元的全年总收入中,游戏机游戏占44%,手机游戏占40%,PC游戏仅占14%,来自流媒体服务的不到2%。 2020年,欧洲市场游戏流媒体服务收入从2019年的3.41亿欧元增长至3.97亿美元,同比增长16.5%。 以上是TcaplusDB收集的游戏行业和数据库行业的近期消息。感谢阅读。 TcaplusDB是腾讯出品的分布式NoSQL数据库,存储和调度的代码完全自主开发。它具有缓存落地融合架构、PB级存储、毫秒级延迟、无损水平扩展、数据结构复杂等特点。同时具有生态丰富、迁移方便、运维成本极低、五九高可用的特点。客户涵盖游戏、互联网、政务、金融、制造、物联网。 原文链接:https://www.icode9.com/content-1-1151427.html
  4. 标签:教程加入等待1 pythonstarttimeprint多线程strftime。 在Python的多线程编程中,我们经常会遇到thread.join()这样的代码。所以今天,让我们用实际代码来解释join的功能。 一、 当一个进程启动时,默认情况下会生成一个主线程,因为线程是程序执行流程的最小单元。当设置多线程时,主线程将创建多个子线程。在python中,默认情况下(实际上是setDaemon(False)),主线程在执行完自己的任务后会退出,子线程会继续执行自己的任务,直到自己的任务完成。有关示例,请参见以下内容。 #测试多线程中join的功能。 导入线程,时间 def DoWaiting(:) 打印“开始等待1:”时间。str time(“% h :% m :% S”)“\ n” 睡眠时间(3) 打印“停止等待1:”时间。str time(“% h :% m :% S”)“\ n” def DoWaiting 1(:) 打印“开始等待2:”时间。str time(“% h :% m :% S”)“\ n” 睡眠时间 打印“停止等待2:”,time . str time(' % h :% m :% S ')”\ n tsk=[] thread1=线程。线程(目标=等待) thread1.start() tsk.append(thread1) thread2=线程。线程(目标=下行1) thread2.start() tsk.append(thread2) 打印“开始加入:”时间. str time(“% h :% m :% S”)“\ n” 打印“end join :”time . str time(“% h :% m :% S”)“\ n” 代码执行结果如下: 开始等待58860 . 88888888886 开始加入38860 . 8888888886 开始等待58860 . 88888888886 端部连接58860 . 8888888886 不要再等了。48860 . 88886886861 不要再等了。48860 . 88886886861 二、 当我们使用setDaemon(True)方法将一个子线程设置为守护线程时,一旦主线程完成执行,所有线程都会被终止。可能会发生这样的情况:子线程的任务在完全执行完之前被迫停止。有关示例,请参见以下内容。 #测试多线程中join的功能。 ''' 学习中的问题没有答案?边肖创建了Python学习交流QQ群:531509025。 寻找志同道合的朋友,互相帮助,群内还有不错的视频学习教程和PDF电子书! ''' 导入线程,时间 def DoWaiting(:) 打印“开始等待1:”时间。str time(“% h :% m :% S”)“\ n” 睡眠时间(3) 打印“停止等待1:”时间。str time(“% h :% m :% S”)“\ n” def DoWaiting 1(:) 打印“开始等待2:”时间。str time(“% h :% m :% S”)“\ n” 睡眠时间 打印“停止等待2:”,time . str time(' % h :% m :% S ')”\ n tsk=[] thread1=线程。线程(目标=等待) 线程1 .设置守护程序(真) thread1.start() tsk.append(thread1) 摆脱阀值 ad2 = threading.Thread(target = doWaiting1) thread2.setDaemon(True) thread2.start() tsk.append(thread2) print 'start join: ' + time.strftime('%H:%M:%S') + "\n" print 'end join: ' + time.strftime('%H:%M:%S') + "\n" 代码执行结果如下: start waiting1: 22:34:46 start waiting2: 22:34:46 start join: 22:34:46 end join: 22:34:46 三、 没有设置守护线程且没有设置join函数的timeout参数时,主线程将会一直等待,直到子线程全部结束,主线程才结束,程序退出。 代码如下: # 测试多线程中join的功能 import threading, time def doWaiting(): print 'start waiting1: ' + time.strftime('%H:%M:%S') + "\n" time.sleep(3) print 'stop waiting1: ' + time.strftime('%H:%M:%S') + "\n" def doWaiting1(): print 'start waiting2: ' + time.strftime('%H:%M:%S') + "\n" time.sleep(8) print 'stop waiting2: ', time.strftime('%H:%M:%S') + "\n" tsk = [] thread1 = threading.Thread(target = doWaiting) thread1.start() tsk.append(thread1) thread2 = threading.Thread(target = doWaiting1) thread2.start() tsk.append(thread2) print 'start join: ' + time.strftime('%H:%M:%S') + "\n" for tt in tsk: tt.join() print 'end join: ' + time.strftime('%H:%M:%S') + "\n" 代码执行结果如下: start waiting1: 22:41:50 start join: 22:41:50 start waiting2: 22:41:50 stop waiting1: 22:41:53 stop waiting2: 22:41:58 end join: 22:41:58 四、 当没有设置守护线程且join函数的参数timeout=2时,主线程将会等待多个子线程timeout的累加和这样的一段时间,时间一到,主线程结束,但是并没有杀死子线程,子线程依然可以继续执行,直到子线程全部结束,程序退出。 代码如下: # 测试多线程中join的功能 ''' 学习中遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' import threading, time def doWaiting(): print 'start waiting1: ' + time.strftime('%H:%M:%S') + "\n" time.sleep(3) print 'stop waiting1: ' + time.strftime('%H:%M:%S') + "\n" def doWaiting1(): print 'start waiting2: ' + time.strftime('%H:%M:%S') + "\n" time.sleep(8) print 'stop waiting2: ', time.strftime('%H:%M:%S') + "\n" tsk = [] thread1 = threading.Thread(target = doWaiting) thread1.start() tsk.append(thread1) thread2 = threading.Thread(target = doWaiting1) thread2.start() tsk.append(thread2) print 'start join: ' + time.strftime('%H:%M:%S') + "\n" for tt in tsk: tt.join(2) print 'end join: ' + time.strftime('%H:%M:%S') + "\n" 代码执行结果如下: start waiting1: 23:02:34 start waiting2: 23:02:34 start join: 23:02:34 stop waiting1: 23:02:37 end join: 23:02:38 stop waiting2: 23:02:42 五、 当设置守护线程join函数的参数timeout=2时,主线程将会等待多个子线程timeout的累加和这样的一段时间,时间一到,主线程结束,杀死未执行完的子线程,程序退出。 代码如下: # 测试多线程中join的功能 ''' 学习中遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' import threading, time def doWaiting(): print 'start waiting1: ' + time.strftime('%H:%M:%S') + "\n" time.sleep(3) print 'stop waiting1: ' + time.strftime('%H:%M:%S') + "\n" def doWaiting1(): print 'start waiting2: ' + time.strftime('%H:%M:%S') + "\n" time.sleep(8) print 'stop waiting2: ', time.strftime('%H:%M:%S') + "\n" tsk = [] thread1 = threading.Thread(target = doWaiting) thread1.setDaemon(True) thread1.start() tsk.append(thread1) thread2 = threading.Thread(target = doWaiting1) thread2.setDaemon(True) thread2.start() tsk.append(thread2) print 'start join: ' + time.strftime('%H:%M:%S') + "\n" for tt in tsk: tt.join(2) print 'end join: ' + time.strftime('%H:%M:%S') + "\n" 代码执行结果如下: start waiting1: 23:23:57 start waiting2: 23:23:57 start join: 23:23:57 stop waiting1: 23:24:00 end join: 23:24:01 原文链接:https://www.icode9.com/content-1-1151430.html
  5. 标签:总结面试题爪哇面试csdnhttpsarticlenet最新版 爪哇面试总结汇总,整理了包括爪哇基础知识,集合容器,并发编程,JVM,常用开源框架春天,我的巴蒂,数据库,中间件等,包含了作为一个爪哇工程师在面试中需要用到或者可能用到的绝大部分知识。 看看我是怎么学爪哇https://pan.baidu.com/s/16GwFh17fihySHvEVt02OkQ提取码:86ir 序号内容链接地址 一个爪哇基础知识面试题(2020最新版)https://think won . blog . csdn . net/article/details/104390612 2 Java集合容器面试题(2020最新版)https://think won . blog . csdn . net/article/details/104588551 3 Java异常面试题(2020最新版)https://think won . blog . csdn . net/article/details/104390689 四并发编程面试题(2020最新版)https://think won . blog . csdn . net/article/details/104863992 5个虚拟机(Java Virtual Machine的缩写)面试题(2020最新版)https://think won . blog . csdn . net/article/details/104390752 6春天面试题(2020最新版)https://think won . blog . csdn . net/article/details/104397516 七弹簧手动音量调节面试题(2020最新版)https://think won . blog . csdn . net/article/details/104397427 8 Spring Boot面试题(2020最新版)https://think won . blog . csdn . net/article/details/1043972999 9春云面试题(2020最新版)https://think won . blog . csdn . net/article/details/104397367 10年面试题(2020最新版)https://think won . blog . csdn . net/article/details/101292950 11 Redis面试题(2020最新版)https://think won . blog . csdn . net/article/details/103522351 12 MySQL数据库面试题(2020最新版)https://think won . blog . csdn . net/article/details/104778621 13 消息中间件(法属)马提尼克岛(马提尼克岛的简写)与RabbitMQ面试题(2020最新版)https://think won . blog . csdn . net/article/details/10458612 14杜布博面试题(2020最新版)https://think won . blog . csdn . net/article/details/104390006 15 Linux面试题(2020最新版)https://think won . blog . csdn . net/article/details/104588679 16只雄猫面试题(2020最新版)https://think won . blog . csdn . net/article/details/104397665 17动物园管理员面试题(2020最新版)https://think won . blog . csdn . net/article/details/104397719 18 Netty面试题(2020最新版)https://think won . blog . csdn . net/article/details/104391081 19 架构设计分布式数据结构与算法面试题(2020最新版)https://think won . blog . csdn . net/article/details/105870730 看看我是怎么学爪哇https://pan.baidu.com/s/16GwFh17fihySHvEVt02OkQ提取码:86ir 原文链接:https://博客。csdn。net/ThinkWin/article/details/103592572 原文链接:https://www.icode9.com/content-1-1151432.html
  6. 标签:39右目标递归nums33问题解决方案在左。 文章目录 33.搜索旋转排序数组-中-9/1734。查找排序数组中元素的第一个和最后一个位置-中-9/1839。组合总和-中等-9/2242。雨水-困难-9/2346。全排列-中等-9/24。 33. 搜索旋转排序数组 - 中等 - 9/17 33.搜索旋转排序数组-中等。 数组nums按升序排列,数组中的值互不相同。 在将它传递给函数之前,nums在未知的下标k(0=k nums.length)上旋转,这样数组就变成了[NUMS [k],NUMS[k ^ 1],…,NUMS [n-1],NUMS [0],NUMS [1],…,NUMS [k-1]。例如,[0,1,2,4,5,6,7]在下标3旋转后可能变成[4,5,6,7,0,1,2]。 给你旋转的数组nums和一个整数目标。如果这个目标值存在于nums中,返回它的下标,否则返回-1。 解法一: 直接遍历搜索 解决方案类{ public int search(int[] nums,int target){ 0 int len=nums.length int I=0; while(ilen){ 0 if(nums[I]==target){ 0 返回I; } 我; } 返回-1; } } 解法二: 二分搜索法:将阵列分成左右两半,判断哪个部分有序,然后判断目标是否在有序部分的范围内。如果没有,继续缩小二进制范围,然后在另一半搜索。 解决方案类{ public int search(int[] nums,int target){ 0 int right=nums . length-1;//右指针 if(right0)返回-1; if(右==0 ) return nums[0]==target? 0:-1; int left = 0; //左指针 int mid = 0; //中位数下标 while(left<=right){ mid = (left+right)/2; //计算中位数 if(nums[mid] == target) return mid; if(nums[left] <= nums[mid]){ //左半边有序 if(target >= nums[left] && target < nums[mid]){ //target在左半边 right = mid - 1; }else{ left = mid + 1; } }else{ //右半边有序 if(target > nums[mid] && target <= nums[right]){//target在右半边 left = mid + 1; }else{ right = mid - 1; } } } return -1; } } 34. 在排序数组中查找元素的第一个和最后一个位置 - 中等 - 9/18 34. 在排序数组中查找元素的第一个和最后一个位置 - 中等 - 9/18 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target,返回 [-1, -1]。 进阶: 你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗? 解析: l 指针掌管左边蓝色区域, rr 指针掌管右边红色区域,两者互不冲突,通过不断向目标元素靠近扩大掌管区域,直到两者掌管区域接壤。 二分查找起始状态: 二分查找终止状态: < 、≤ 、≥ 、> 目标元素对应的蓝红区域划分 总结模板: class Solution { public int[] searchRange(int[] nums, int target) { //左边界 int leftIdx = binarySearchLeft(nums, target); //右边界 int rightIdx = binarySearchRight(nums, target); //判断是否符合 if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] == target && nums[rightIdx] == target) { return new int[]{leftIdx, rightIdx}; } return new int[]{-1, -1}; } //找左边界的二分查找 public int binarySearchLeft(int[] nums, int target) { //注意左右指针的初始值 int left = -1, right = nums.length; while (left+1 != right) { int mid = (left + right) / 2; if (nums[mid] >= target) { right = mid; } else { left = mid; } } return right; } //找右边界的二分查找 public int binarySearchRight(int[] nums, int target) { //注意左右指针的初始值 int left = -1, right = nums.length; while (left+1 != right) { int mid = (left + right) / 2; if (nums[mid] <= target) { left = mid; } else { right = mid; } } return left; } } 时间复杂度:O(log(n))。 空间复杂度:O(1)。 39. 组合总和 - 中等 - 9/22 39. 组合总和 - 中等 - 9/22 给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。 candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。 对于给定的输入,保证和为 target 的唯一组合数少于 150 个。 解析: 先向前列举所有情况,得到一个解或者走不通的时候就回溯。 class Solution { List<List<Integer>> res; public List<List<Integer>> combinationSum(int[] candidates, int target) { //存放结果 res = new ArrayList<>(); //排序 Arrays.sort(candidates); //回溯 backtrack(candidates,target,new ArrayList<>(),0); return res; } //回溯 private void backtrack(int[] candidates,int remains,List<Integer> path,int start){ //如果做减法后剩下0,则把这条路径添加进来 if(remains == 0){ res.add(new ArrayList<>(path)); return; } //遍历 for(int i=start; i<candidates.length; i++){ //如果剩余值小于0,就返回 if(remains-candidates[i] < 0) return; //剪枝 即去掉相同数的路径 如[2,2,3,7] 去掉第二个2。 if(i > 0 && candidates[i] == candidates[i-1]) continue; //添加元素到路径 path.add(candidates[i]); //回溯 backtrack(candidates, remains-candidates[i], path, i); //找到了一个解或者 remains < 0 了,将当前数字移除,然后继续尝试 path.remove(path.size()-1); } } } 42. 接雨水 - 困难 - 9/23 42. 接雨水 - 困难 - 9/23 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 方法一:双指针 class Solution { public int trap(int[] height) { int left = 0, right = height.length - 1; //定义左右指针 int ans = 0; //结果 int left_max = 0, right_max = 0; //左右当前的最大值 while(left<right){ if(height[left] < height[right]){ //如果左边高度比右边小 if (height[left] >= left_max){ //如果左边当前值 大于等于 左边最大值,则更新左边最大值 left_max = height[left]; } else { //否则 就可累加计算雨水量 ans += (left_max - height[left]); } ++left; //移动左指针 } else { //如果左边高度比右边大 if(height[right] >= right_max){ //如果右边当前值 大于等于 右边最大值,则更新右边最大值 right_max = height[right]; } else { //否则 就可累加计算雨水量 ans += (right_max - height[right]); } --right; //移动右指针 } } return ans; } } 时间复杂度:O(n)。 空间复杂度:O(1)。 方法二:动态规划 class Solution { public int trap(int[] height) { int len = height.length; //长度 if(len == 0){ return 0; } //从左往右遍历,记录左边最高值 int[] left_max = new int[len]; left_max[0] = height[0]; for(int i=1; i<len; i++){ left_max[i] = Math.max(left_max[i-1],height[i]); } //从右往左遍历,记录右边最高值 int[] right_max = new int[len]; right_max[len-1] = height[len-1]; for(int j=len-2; j>=0; j--){ right_max[j] = Math.max(right_max[j+1],height[j]); } //计算接水量 int sum = 0; for(int k=0; k<len; k++){ sum += Math.min(left_max[k],right_max[k]) - height[k]; } return sum; } } 时间复杂度:O(n),其中n是数组height的长度。计算数组leftMax和rightMax的元素值各需要遍历数组height一次,计算能接的雨水总量还需要遍历一次。 空间复杂度:O(n),其中n是数组height的长度。需要创建两个长度为n的数组leftMax 和 rightMax。 46. 全排列 - 中等 - 9/24 46. 全排列 - 中等 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 解析:回溯法 利用递归每次向 temp 里添加一个数字,数字添加够以后再回来进行回溯,再向后添加新的解。 可以理解成一层一层的添加,每一层都是一个 for 循环。 每调用一层就进入一个 for 循环,相当于列出了所有解,然后挑选了我们需要的。其实本质上就是深度优先遍历 DFS。 class Solution { public List<List<Integer>> permute(int[] nums) { List<List<Integer>> res = new ArrayList<>(); //存放结果 backtrack(nums,res,new ArrayList<>()); //回溯 return res; //返回结果 } private void backtrack(int[] nums,List<List<Integer>> res, List<Integer> tempList){ if(tempList.size() == nums.length){ //如果临时列表长度 等于 数组的长度 就添加并返回 res.add(new ArrayList<>(tempList)); return; } for(int i=0; i<nums.length; i++){ if(tempList.contains(nums[i])) continue; //如果已经存在元素,跳过 tempList.add(nums[i]); //添加元素 backtrack(nums,res,tempList); //回溯 向后继续添加 tempList.remove(tempList.size() - 1); //将 tempList 刚添加的元素,去掉,尝试新的元素 } } } 分析: 递归之前 => [1] 递归之前 => [1, 2] 递归之前 => [1, 2, 3] 递归之后 => [1, 2] 递归之后 => [1] 递归之前 => [1, 3] 递归之前 => [1, 3, 2] 递归之后 => [1, 3] 递归之后 => [1] 递归之后 => [] 递归之前 => [2] 递归之前 => [2, 1] 递归之前 => [2, 1, 3] 递归之后 => [2, 1] 递归之后 => [2] 递归之前 => [2, 3] 递归之前 => [2, 3, 1] 递归之后 => [2, 3] 递归之后 => [2] 递归之后 => [] 递归之前 => [3] 递归之前 => [3, 1] 递归之前 => [3, 1, 2] 递归之后 => [3, 1] 递归之后 => [3] 递归之前 => [3, 2] 递归之前 => [3, 2, 1] 递归之后 => [3, 2] 递归之后 => [3] 递归之后 => [] 输出的结果:[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] 原文链接:https://www.icode9.com/content-1-1151434.html
  7. Url系统生成源代码,以缩短标签:.的PHP链接 简介: 新的PHP短URL系统URL缩短平台使您能够轻松缩短链接,根据受众的位置或平台定位受众,并为缩短的链接提供分析见解。 该系统采用laravel框架编写,前后语言均可使用。它可以设置多个域名和设置包,值得使用。 源代码下载地址https://www.skpan.cn/OSF3tbG0CJS. 图片: 原文链接:https://www.icode9.com/content-1-1151443.html
  8. 标签:javaname定制时代构建面向对象的Studentpublic。 概述 一个类至少有一个构造函数,可以自定义多个构造函数。 作用 创建一个对象并初始化该对象(创建对象时会调用构造函数)。 特点 构造函数名和类名相同。 定义返回值类型。 构造函数中不需要返回。 默认构造器 每个类都有一个没有参数和方法体的默认构造函数。 public 类名() {} 自定义构造器 自定义构造函数,默认构造函数消失。 构造函数可以被重载(使用相同的名称但是不同的参数)。 1个公共班级学生{ 2字符串名称; 3国际年龄; 四 5 //自定义构造函数。 6公共学生(字符串名称,整数){ 0 7//这指向上面的名字。 8 this.name=name 年龄=年龄; 10 } 11 12 void say(){ 0 13 system . out . println(' name : ' name ' age : ' age); 14 } 15 16 } 17 18级测试2( 19公共静态void main(String[]args){ 0 20学生st1=新生('李静',47); 21 st1 . say(); 22 } 23 24 } 原文链接:https://www.icode9.com/content-1-1151446.html
  9. 标签:unappgetuserinfo微信用户头像授权getUserProfile。 uniapp 微信小程序授权登录getUserInfo获取不到用户的昵称和头像了 今天上班的时候高兴的说要等下午上线,然后发现用户授权信息快不行了。uniapp小程序授权登录时,我无法获取用户的头像等信息,只能得到灰色默认头像和昵称为“微信用户”。这是一个多么尴尬的问题。直接说问题和解决方法吧。 1.以前授权用户头像的昵称方法: 按钮打开-类型=' GetUserInfo ' @ GetUserInfo=' GetUserInfo '/按钮 但是现在,我们只能得到一个默认的灰色头像,昵称=“微信用户”,两个数据,其他年龄和地区等等,更别说一个授权弹出来了! 看到官网下图就知道API已经更新了: 用户信息获取界面发生了变化,取而代之的是wx.getUserProfile(对象对象)。 https://developers . weixin . QQ.com/mini program/dev/API/open-API/user-info/wx . getuser profile . html 定位增加了频率限制。 https://developers . weixin . QQ.com/community/developer/doc/000 aee 91 a 98d 206 BC 6 DBE 722 b 51801?blockType=1 2、直接说解决方法吧 查很多网页,尝试各种方法! 最后发现官方公告,称getUserInfo()系列将在4月13日之后激活。至于它为什么先进。 这意味着你现在不能使用getUserInfo! 正确的使用方法是把uni.getUserProfile()作为普通接口调用! //注意不要再开了——类型='getUserInfo '。 Text @click='getUserInfo '请登录/text。 脚本 getUserInfo(){ 0 Console.log(“一旦成功”) uni . GetUserProfile({ 0 描述:“wexin”。//此参数是必需的。 成功: RES={ 0 } console.log(res) }, fail : err={ 0 console.log(错误) } }) } /script 有效尝试,不加班哈哈哈。 原文链接:https://www.icode9.com/content-1-1151466.html
  10. 标签:新基奇爪哇下划线新地图首字母sbfsplitString (将如left lower lung lobe 左下方肺叶转换为testTbKkkLlll) 公共静态映射字符串,对象zh(映射字符串,对象映射){ 0 HashMapString,Object newMap=new HashMapString,Object(); 对于(EntryString,对象条目:地图。entryset()){ 0 字符串键=条目。GetKey(); 字符串值=(字符串)条目。getvalue(); string new key=key。tolowercase(); StringBuffer sbf=new StringBuffer(); if (newKey.contains('_ ')) { //按下划线来切割字符串为数组 string[]split=NewKey。拆分(' _ '); //循环数组操作其中的字符串 for (int i=0,index=split.length一级指数;I){ 0 char[] ch=split[i].toCharArray(); if(i0){ 0 ch[0]=(char)(ch[0]-32); } //添加到字符串缓冲区 sbf。追加(ch); } }else{ sbf。追加(NewKey); } newMap.put(sbf.toString(),值); } 返回新地图 } 原文链接:https://www.icode9.com/content-1-1151471.html
  11. 标签:5.1 one phpindexprequesttprewritesecond 我用的是集成服务环境简体中文版框架thinkphp 5.1 我按照开发手册进行配置,隐藏index.php访问时,出现"没有指定输入文件。" 配置过程: 1.更改街头流氓服务器配置文件httpd.conf 去掉" mod _ rewrite.so "前面的# 2."允许无"将没有人改为全部 3 .公共文件夹下的。文件的内容为: IfModule mod_rewrite.c 符号链接后的选项 重写引擎打开 重写第%{REQUEST_FILENAME}秒!-d 重写第%{REQUEST_FILENAME}秒!-f #重写者^(.*)$ index.php/$1[QSA,PT,L] 重写器^(.*)$ index.php?s=$1 [QSA,PT,L] /IfModule 原文链接:https://www.icode9.com/content-1-1151472.html
  12. 标签:特写-torchmodupeltorch来源设备平衡部分。 [源码解析] PyTorch 流水线并行实现 (2)--如何划分模型 pytorch管道的并行实现(2)——如何划分模型0x00抽象0x01问题0x01自动平衡1.1自动平衡1.2基本功能/1.2.1批处理1.2.2 layer wise _ sandbox 1.2.3拆离1.3根据计算时间进行平衡1.4根据内存大小进行平衡1.5划分算法0x02模型划分2.1调用2.2 GPipe构建2.3示例2.4总结0xFF参考。 0x00 摘要 在上一篇文章中,我们介绍了PyTorch管道并行的基础知识,在本文中,我们介绍了它的自动平衡机制和模型分割。 其他平行文章链接如下: 【源代码分析】深度学习流水线并行gpipe(1)——流水线的基本实现。 【源代码分析】深度学习流水线并行GPipe(2)——梯度积累。 【源代码分析】深度学习流水线并行GPipe(3)——重算。 【源代码分析】pipe dream(1)-深度学习流水线并行的概要阶段。 【源代码分析】深度学习流水线并行pipe dream(2)——计算分区。 【源代码分析】深度学习流水线并行pipe dream(3)-转换模型。 【源代码分析】深度学习流水线并行pipe dream(4)-运行时引擎。 【源代码分析】深度学习流水线并行pipe dream(5)-通信模块。 【源代码分析】深度学习流水线并行pipedream (6)-1f1b策略。 【源代码分析】pytorch管道的并行实现(1)——基础知识。 本文来源于论文和github源代码。 0x01 问题 流水线面临的第一个问题是: 如何把一个大模型分成几个小模型?分割的算法是什么? 如何将这些小型号分配给多个设备?的分配算法是什么? 如何实现最佳或近似最佳的整体性能?衡量标准是什么? 比如一个六层的大模型怎么能分成三个小模型? - | | |第1层-第2层-第3层-第4层-第5层-第6层| | | - - | | | ? | | v - ----+ +---------------------+ +--------------------+ |Device 1 | |Device 2 | |Device 3 | | | | | | | | Layer 1 | +---------> Layer 4 | | | | + | | | + | +-------> Layer 6 | | | | | | | | | | | | v | | | | | | | | | Layer 2 | | | | | | | | | + | | | v | | | | | | | | | Layer 5 +---------+ | | | v | | | | | | | Layer 3 +---------+ | | | | | | | | | | +--------------------+ +---------------------+ +--------------------+ 接下来,我们就看看 torchgpipe 是如何解决这些问题的。 0x01 自动平衡 torchgpipe提供了子模块 torchgpipe.balance 来计算得到分区,目的是让两两分区(pairwise)之间的资源差别尽量小。资源占用情况是通过分析(profile)来计算。 1.1 Automatic Balancing 切分模型会影响GPU的利用率,比如其中计算量较大的层会减慢下游的速度,所以需要找到一个模型的最佳平衡点。但是,确定模型的最佳平衡点是很难的,特别是,如果用户仍在设计模型阶段,则模型体系结构可能会随着时间的推移而改变。在这种情况下,TorchPipe 强烈建议使用 torchgpipe.balance来自动平衡。这不会给用户提供最佳的平衡,但这是一个足够好的平衡。 请注意,这个功能是由torchgpipe提供的,而不是来自Huang等人的GPipe 原始论文。 torchgpipe提供了两个平衡工具,两者都基于每层的profile结果来使用,用户可以根据需要选择平衡工具。 ~torchgpipe.balance.balance by_time:跟踪每层的运行时间。 ~torchgpipe.balance.balance by_size:检测每层的CUDA内存使用情况。 具体使用方式如下,用户需要向模型中输入一个样本输入。 from torchgpipe import GPipe from torchgpipe.balance import balance_by_time partitions = torch.cuda.device_count() sample = torch.rand(128, 3, 224, 224) # 用户需要向模型中输入一个样本输入 balance = balance_by_time(partitions, model, sample) model = GPipe(model, balance, chunks=8) 1.2 基础函数/函数 1.2.1 Batch Batch 是一个基础类,位于 torchgpipe/microbatch.py,其作用是把 tensor 或者 tensors 封装起来做统一处理。Batch 把张量保存在自己的 value 成员变量之中。在调用 call 方法时候,就把传入的方法应用到 value 张量之上。 比如后面我们会讲到的 Pipeline.compute 方法之中会有如下,就是把 partition 应用到 batch 内的张量之上: def compute(batch: Batch = batch, partition: nn.Sequential = partition, skip_tracker: SkipTrackerThroughPotals = skip_trackers[i], ) -> Batch: with use_skip_tracker(skip_tracker): return batch.call(partition) task = Task(streams[j], compute=compute, finalize=None) 具体 Batch 定义如下: Tensors = Tuple[Tensor, ...] TensorOrTensors = Union[Tensor, Tensors] Function = Callable[[TensorOrTensors], TensorOrTensors] class Batch: """An abstraction of an atomic tensor or a tuple of tensors. This eliminates every boilerplate code to classify an atomic tensor or a tuple of tensors. :: x = generate_tensor_or_tensors() x = Batch(x) # in-place update x[0] = F.apply(x[0]) x[:] = F.apply(*x) # f(x) if x is a tensor. # f(*x) if x is a tuple of tensors. # y is also a batch. y = x.call(f) """ def __init__(self, value: TensorOrTensors) -> None: self.value = value self.atomic = torch.is_tensor(value) @property def tensor(self) -> Tensor: """Retrieves the underlying tensor.""" if not self.atomic: raise AttributeError('not atomic batch') return cast(Tensor, self.value) @property def tensors(self) -> Tensors: """Retrieves the underlying tensors.""" if self.atomic: raise AttributeError('batch is atomic') return cast(Tensors, self.value) @property def tensor_or_tensors(self) -> TensorOrTensors: """Retrieves the underlying tensor or tensors regardless of type.""" return self.value def call(self, function: Function) -> 'Batch': # 这里是关键方法 """Calls a function by the underlying tensor or tensors. It also wraps the output with :class:`Batch`. """ return Batch(function(self.value)) # 调用模型的forward 1.2.2 layerwise_sandbox layerwise_sandbox 方法的作用是在不影响原有模型的基础上,拷贝模型的层,这样更容易profile。 def layerwise_sandbox(module: nn.Sequential, device: torch.device, ) -> Generator[nn.Module, None, None]: """Copies layers for ease to profile. It doesn't modify the given module. """ for layer in module: layer_copy = copy.deepcopy(layer) layer_copy.to(device) layer_copy.train() yield layer_copy 1.2.3 detach detach 方法的作用是从autograd图中detach一些张量,得到一组新的张量。这些张量从当前计算图中被分离下来。但是仍指向原变量的存放位置。detach 可以切断一些分支的反向传播.。 def detach(batch: Batch) -> None: """Detaches from autograd graph.""" for i, x in enumerate(batch): batch[i] = x.detach().requires_grad_(x.requires_grad) 在torchgpipe代码中,经常可以见到 detach 的使用,这个从注释可以看出来,是因为 PyTorch 的一个bug 而采取的workround。 # A Python autograd function might fail with this error: # # RuntimeError: Returning Variables sharing storage with other Variables # that require grad is not supported in Python functions. Please submit a # feature request if you hit this error. # # It doesn't look like an essential restriction. But it happens on the # current PyTorch version. To avoid it, we should detach the tensor before # returning by identity autograd functions, such as Wait, Fork, and Join. # 1.3 据计算时间来平衡 balance_by_time 方法的作用就是依据运行时间来平衡,其中参数如下: partitions :分区数目 module : 需要分区的顺序模型 sample :给定 batch size 的样本 其实就是调用 profile_times 依据sample来得到运行时间,然后进行分区。 def balance_by_time(partitions: int, module: nn.Sequential, sample: TensorOrTensors, *, timeout: float = 1.0, device: Device = torch.device('cuda'), ) -> List[int]: """Naive automatic balancing by elapsed time per layer. :: sample = torch.empty(128, 3, 224, 224) balance = balance_by_time(torch.cuda.device_count(), model, sample) gpipe = GPipe(model, balance, chunks=8) Args: partitions (int): intended number of partitions module (torch.nn.Sequential): sequential module to be partitioned sample (torch.Tensor): example input with arbitrary batch size Keyword Args: timeout (float): profiling iterates again if the timeout (in second) is not exceeded (default: ``1.0``) device ('cpu' or 'cuda' device): CPU or CUDA device where each layer is profiled (default: the current CUDA device) Returns: A list of number of layers in each partition. Use it for the `balance` parameter of :class:`~torchgpipe.GPipe`. .. note:: `module` and `sample` must be placed on the same device. """ times = profile_times(module, sample, timeout, torch.device(device)) return balance_cost(times, partitions) 这里的 Batch 类就是对张量或者张量数组进行封装,可以统一使用其方法。 profile_times 依据sample来得到运行时间,具体逻辑是: 遍历模型中的层,针对每个层: 等待当前设备上所有流中的所有kernel完成 记录起始运行时间 对某层进行前向计算 得到需要梯度的张量,如果存在,则进行后向计算 等待当前设备上所有流中的所有kernel完成 记录终止时间 最后返回一个每层运行时间列表。 def profile_times(module: nn.Sequential, sample: TensorOrTensors, timeout: float, device: torch.device, ) -> List[int]: """Profiles elapsed times per layer.""" if any(p.grad is not None for p in module.parameters()): raise ValueError('some parameter already has gradient') _batch = Batch(sample) for i, x in enumerate(_batch): _batch[i] = x.detach().to(device).requires_grad_(x.requires_grad) time_bufs: List[List[float]] = [[] for _ in module] begun_at = time.time() while time.time() - begun_at < timeout: batch = _batch # 遍历模型中的层 for i, layer in enumerate(layerwise_sandbox(module, device)): detach(batch) if device.type == 'cuda': torch.cuda.synchronize(device) # 等待当前设备上所有流中的所有kernel完成 tick = time.time()# 起始运行时间 # Forward batch = batch.call(layer) # 对某层进行前向计算 # Backward # 得到需要梯度的张量 backward_tensors = tuple(y for y in batch if y.requires_grad) # 进行后向计算 if backward_tensors: torch.autograd.backward(backward_tensors, backward_tensors) if device.type == 'cuda': torch.cuda.synchronize(device) # 等待当前设备上所有流中的所有kernel完成 tock = time.time() # 终止时间 time_bufs[i].append(tock - tick) us = 1_000_000 return [sum(int(t*us) for t in buf) for buf in time_bufs] 1.4 据内存大小来平衡 balance_by_size 方法的作用就是依据运行时内存大小来平衡,其中参数如下: partitions :分区数目,从示例看,可以认为是设备数。 module : 需要分区的顺序模型 sample :给定 batch size 的样本 其实就是调用 profile_sizes 依据sample来得到运行时内存大小,然后进行分区。 在训练期间,参数所需的内存取决于使用哪个优化器。优化器可以为每个参数使用缓冲区来在其内部跟踪优化统计信息,例如SGD中的动量缓冲区。 为了获得更可靠的基于大小的平衡,用户应该为优化器指定相应的“param_scale”。默认的“param_scale”是2,而不是1,这是因为梯度累积(gradient accumulation)是每个优化器所必需的。下面注释之中也给出了一些参考取值。 def balance_by_size(partitions: int, module: nn.Sequential, input: TensorOrTensors, *, chunks: int = 1, param_scale: float = 2.0, device: Device = torch.device('cuda'), ) -> List[int]: """Naive automatic balancing by CUDA memory usage per layer. During training, required memory for parameters depends on which optimizer is used. Optimizers may use buffers for each parameter to track optimization statistics internally, such as momentum buffer in SGD. To get more reliable size based balance, you should specify `param_scale` with regard to your optimizer. The default `param_scale` is 2 instead of 1 due to gradient accumulation which is necessary for every optimizer. Follow this guide to choose correct `param_scale` for typical optimizers: ========= ============= ========================================= Optimizer `param_scale` Internal State ========= ============= ========================================= SGD 2--3 (momentum_buffer) Adam 4--5 exp_avg, exp_avg_sq, (max_exp_avg_sq) Adadelta 4 square_avg, acc_delta Adagrad 3 sum RMSprop 3--5 square_avg, (momentum_buffer), (grad_avg) ========= ============= ========================================= Here's a simple example with the Adam optimizer:: balance = balance_by_size( torch.cuda.device_count(), model, # Same size with mini-batch to train torch.empty(1024, 3, 224, 224), # Number of micro-batches to train with GPipe chunks=8, # 4 for Adam param_scale=4.0, ) gpipe = GPipe(model, balance, chunks=8) adam = Adam(gpipe.parameters()) Args: partitions (int): intended number of partitions module (torch.nn.Sequential): sequential module to be partitioned input (torch.Tensor): example mini-batch with the same size to train Keyword Args: chunks (int): number of micro-batches will be used to train (default: ``1``) param_scale (float): how many copies of parameters would be allocated for training. It depends on optimizer. See the above guide. (default: ``2.0``) device ('cuda' device): CUDA device where each layer is profiled (default: the current CUDA device) Returns: A list of number of layers in each partition. Use it for the `balance` parameter of :class:`~torchgpipe.GPipe`. .. note:: `module` and `input` must be placed on the same CUDA device. """ sizes = profile_sizes(module, input, chunks, param_scale, torch.device(device)) return balance_cost(sizes, partitions) profile_sizes 逻辑如下: 遍历模型中的层,针对每个层: 使用 torch.cuda.memory_allocated 计算前向传播用到的显存,就是激活值。torch.cuda.memory_allocated(device=None) 返回给定设备device的张量所占用的当前GPU内存。 使用 p.storage().size() * p.storage().element_size() 计算参数尺寸。 pytorch中的storage指的是连续的内存块,而tensor可以认为是映射到storage的视图。 element_size() 返回单个元素的字节。 把激活值和参数加在一起,插入列表。 返回内存大小列表。 def profile_sizes(module: nn.Sequential, input: TensorOrTensors, chunks: int, param_scale: float, device: torch.device, ) -> List[int]: """Profiles CUDA memory usage per layer.""" if device.type != 'cuda': raise ValueError('size profiler supports only CUDA device') batch = Batch(input) sizes: List[int] = [] latent_scale = batch[0].size(0) / chunks for i, x in enumerate(batch): batch[i] = x[:1].detach().to(device).requires_grad_(x.requires_grad) for layer in layerwise_sandbox(module, device): detach(batch) # Detect memory usage at forward. # 计算前向传播用到的显存,就是激活值 memory_before = torch.cuda.memory_allocated(device) batch = batch.call(layer) # 对某层进行前向传播 memory_after = torch.cuda.memory_allocated(device) latent_size = memory_after - memory_before # Analyze size of parameters. # 计算参数尺寸 param_size = sum(p.storage().size() * p.storage().element_size() for p in layer.parameters()) # 把激活值和参数加在一起,插入列表 # Combine size of parameters and activations with normalize scales. size = latent_size*latent_scale + param_size*param_scale sizes.append(int(size)) return sizes # 返回内存大小列表 1.5 分割算法 得到每层的计算时间或者内存大小之后,会通过如下代码来进行具体分割。 times = profile_times(module, sample, timeout, torch.device(device)) return balance_cost(times, partitions) 具体 balance_cost 只是一个封装而已,算法还是 blockpartition.solve。 def balance_cost(cost: List[int], partitions: int) -> List[int]: partitioned = blockpartition.solve(cost, partitions) return [len(p) for p in partitioned] 从其注释可知,blockpartition.solve 实现了这篇论文的算法。 Implements "Block Partitions of Sequences" by Imre Bárány et al.Paper: https://arxiv.org/pdf/1308.2452.pdf 这是一篇数学论文,其算法伪代码如下(与后续实现中注释基本一一对应)。 该论文是纯粹的数学论证,我们不去研究其内部机制,只是看看其运行结果。 我们回忆一下,这里支持的模型是顺序模型,所以无论时间还是内存大小,都是一个list。solve的作用就是把这个list尽量平均分配成若干组。 假设模型有6层,需要分配到两个device之上,那么应该如何分割呢? blockpartition.solve([1, 2, 3, 4, 5, 6], partitions=2) 结果是 [[1, 2, 3, 4], [5, 6]] 如果分成三个device,则: solve([1, 2, 3, 4, 5, 6], partitions=3) 结果是 [[1, 2, 3], [4, 5], [6]] 然后 balance_cost 会获得每一个 partition 的具体层数,得到balance的最终是: [3,2,1] 分区算法具体代码如下,有兴趣的朋友可以结合论文仔细研究。 def solve(sequence: List[int], partitions: int = 1) -> List[List[int]]: """Splits a sequence into several partitions to minimize variance for each partition. The result might not be optimal. However, it can be done only in O(kn³), where k is the number of partitions and n is the length of the sequence. """ if partitions < 1: raise ValueError(f'partitions must be a positive integer ({partitions} < 1)') n = len(sequence) if n < partitions: raise ValueError(f'sequence is shorter than intended partitions ({n} < {partitions})') # Normalize the sequence in [0, 1]. minimum = min(sequence) maximum = max(sequence) - minimum normal_sequence: List[float] if maximum == 0: normal_sequence = [0 for _ in sequence] else: normal_sequence = [(x-minimum)/maximum for x in sequence] splits = [n//partitions * (x+1) for x in range(partitions-1)] + [n] def block_size(i: int) -> float: start = splits[i-1] if i > 0 else 0 stop = splits[i] return sum(normal_sequence[start:stop]) def leaderboard() -> Iterator[Tuple[float, int]]: return ((block_size(i), i) for i in range(partitions)) while True: """ (1) Fix p ∈ [k] with M(P) = bp. So Bp is a maximal block of P. """ # max_size: M(P) max_size, p = max(leaderboard()) while True: """ (2) If M(P) ≤ m(P) + 1, then stop. """ # min_size: m(P) min_size, q = min(leaderboard()) if max_size <= min_size + 1: return [sequence[i:j] for i, j in zip([0]+splits[:-1], splits)] """ (3) If M(P) > m(P) + 1, then let m(P) = bq for the q ∈ [k] which is closest to p (ties broken arbitrarily). Thus Bq is a minimal block of P. Let Bh be the block next to Bq between Bp and Bq. (Note that Bh is a non-empty block: if it were, then m(P) = 0 and we should have chosen Bh instead of Bq.) """ if p < q: """ So either p < q and then h = q−1 and we define P ∗ by moving the last element from Bh = Bq−1 to Bq, """ h = q - 1 splits[h] -= 1 else: """ or q < p, and then h = q + 1 and P ∗ is obtained by moving the first element of Bh = Bq+1 to Bq. """ h = q + 1 splits[q] += 1 """ Set P = P ∗ . If p = h, then go to (1), else go to (2). """ if p == h: break 0x02 模型划分 2.1 调用 既然得到了 profile 的结果,下面就是对模型的各个层进行分割。如何分割可以参见下面注释中的使用示例,把balance 作为参数传递给 GPipe构造函数。 ''' If your model is still under development, its optimal balance would change frequently. In this case, we highly recommend 'torchgpipe.balance' for naive automatic balancing: from torchgpipe import GPipe from torchgpipe.balance import balance_by_time partitions = torch.cuda.device_count() sample = torch.empty(...) balance = balance_by_time(partitions, model, sample) model = GPipe(model, balance, ...) ''' 2.2 GPipe构建 Gpipe 的 __init__中可以看到,使用了 split_module 函数进行分割: def __init__(self, module: nn.Sequential, balance: Optional[Iterable[int]] = None, *, devices: Optional[Devices] = None, chunks: int = chunks, checkpoint: str = checkpoint, spawn_workersdeferred_batch_norm: bool = False, ) -> None: super().__init__() chunks = int(chunks) checkpoint = str(checkpoint) verify_module(module) # Verify if the underlying skippable modules satisfy integrity. The # integrity can be verified before forward() because it is static. verify_skippables(module) self.chunks = chunks self.checkpoint = checkpoint if deferred_batch_norm: module = DeferredBatchNorm.convert_deferred_batch_norm(module, chunks) if devices is None: devices = range(torch.cuda.device_count()) devices = [torch.device(d) for d in devices] devices = cast(List[torch.device], devices) try: # 对模型进行切分 self.partitions, self.balance, self.devices = split_module(module, balance, devices) except BalanceError as exc: raise ValueError(recommend_auto_balance(str(exc))) self._copy_streams: List[List[AbstractStream]] = [] self._skip_layout = inspect_skip_layout(self.partitions) 所以我们看看 split_module 函数,其主要逻辑如下: 遍历模型包含的层 把新的层加入到数组layers中 如果数组大小等于balance[j],就是达到了device j应该包含的层数,则: 把分区数组构建成一个sequential module,得到变量 partition。 利用 partition.to(device) 把partition放置到相关设备之上,这就是前文提到的,~torchgpipe.GPipe使用CUDA进行训练。用户不需要自己将模块移动到GPU,因为~torchgpipe.GPipe自动把每个分区移动到不同的设备上。 把这个partition加入到分区数组中 然后去下一个device看看 最后返回 partitions, balance, devices。 def split_module(module: nn.Sequential, balance: Iterable[int], devices: List[torch.device], ) -> Tuple[List[nn.Sequential], List[int], List[torch.device]]: """Splits a module into multiple partitions. Returns: A tuple of (partitions, balance, devices). Partitions are represented as a :class:`~torch.nn.ModuleList` whose item is a partition. All layers in a partition are placed in the same device. Raises: BalanceError: wrong balance IndexError: the number of devices is fewer than the number of partitions. """ balance = list(balance) j = 0 partitions = [] layers: NamedModules = OrderedDict() for name, layer in module.named_children(): # 遍历模型包含的层 layers[name] = layer # 把新的层加入到数组中 if len(layers) == balance[j]: # 如果数组大小等于balance[j],就是达到了device j应该包含的层数 # Group buffered layers as a partition. partition = nn.Sequential(layers) # 把层数组组合成一个sequential module device = devices[j] partition.to(device) # 把层放置到相关设备之上 partitions.append(partition) # 这个新module加入到分区数组中 # Prepare for the next partition. layers.clear() j += 1 # 去下一个device看看 partitions = cast(List[nn.Sequential], nn.ModuleList(partitions)) del devices[j:] return partitions, balance, devices 结合上面例子,balance 如下: [3,2,1] 所以前三个层 [1, 2, 3] 组合成一个module,中间两个层 [4, 5] 组合成一个 module,最后层 [6] 是一个module。 最后分区数组为: [ module([1, 2, 3]), module([4, 5]), module([6])] 2.3 示例 我们再具体打印输出看看,模型包含了6个层,分为 3 个partitions,分区内的层数分别是:3个,2个,1个。 a = nn.Linear(1, 1) b = nn.Linear(1, 1) c = nn.Linear(1, 1) d = nn.Linear(1, 1) e = nn.Linear(1, 1) f = nn.Linear(1, 1) balance = [3,2,1] model = nn.Sequential(a, b, c, d, e, f) print(model) model = GPipe(model, balance, devices=['gpu', 'gpu','gpu']) print(model) 结果如下,可以看到原模型被分成3个partition,每个 partition 都是一个Sequential。 Sequential( (0): Linear(in_features=1, out_features=1, bias=True) (1): Linear(in_features=1, out_features=1, bias=True) (2): Linear(in_features=1, out_features=1, bias=True) (3): Linear(in_features=1, out_features=1, bias=True) (4): Linear(in_features=1, out_features=1, bias=True) (5): Linear(in_features=1, out_features=1, bias=True) ) GPipe( (partitions): ModuleList( (0): Sequential( (0): Linear(in_features=1, out_features=1, bias=True) (1): Linear(in_features=1, out_features=1, bias=True) (2): Linear(in_features=1, out_features=1, bias=True) ) (1): Sequential( (3): Linear(in_features=1, out_features=1, bias=True) (4): Linear(in_features=1, out_features=1, bias=True) ) (2): Sequential( (5): Linear(in_features=1, out_features=1, bias=True) ) ) ) 运行时变量如下: model = {GPipe: 6} balance = {list: 3} [3, 2, 1] checkpoint = {str} 'except_last' chunks = {int} 1 devices = {list: 3} 0 = {device} gpu 1 = {device} gpu 2 = {device} gpu partitions = {ModuleList: 3} _modules = '0' = {Sequential: 3} Sequential( (0): Linear(in_features=1, out_features=1, bias=True) (1): Linear(in_features=1, out_features=1, bias=True) (2): Linear(in_features=1, out_features=1, bias=True)) '1' = {Sequential: 2} Sequential( (3): Linear(in_features=1, out_features=1, bias=True) (4): Linear(in_features=1, out_features=1, bias=True)) '2' = {Sequential: 1} Sequential( (5): Linear(in_features=1, out_features=1, bias=True)) 需要注意一点:GPipe 的 partitions 成员变量是 nn.ModuleList 类型。nn.ModuleList是一个容器,其储存不同 module,并自动将每个 module 的 parameters 添加到网络中。但是nn.ModuleList 并没有定义一个网络,而只是将不同的模块储存在一起,这些模块之间并没有什么先后顺序,网络的执行顺序是根据 forward 函数来决定的。 随之而来问题就是:partition内部可以用Sequential来进行一系列的前向操作,但是如何配置partitions 之间的执行顺序?这个我们会在后续文章中分析。 2.4 总结 最后总结一下,流程是从上至下。 使用 balance_by_size 或者 balance_by_time 来先运行系统,得到 profile 结果。 然后使用 split_module 来对模型进行分割。 最后就得到了一个相对平衡的分区结果。 把这些分区分配到不同的设备之上。 具体如下图: +-----------------------------------------------------------------------------------------+ | | | Layer 1 +---> Layer 2 +-----> Layer 3 +-----> Layer 4 +-----> Layer 5 +---> Layer 6 | | | +--------------------------+---------------------------+----------------------------------+ | | balance_by_size | 1 1 | balance_by_time | | v v [[1, 2, 3], [4, 5], [6]] [[1, 2, 3, 4], [5, 6]] + + | | +-----------+ +--------+ | | v v 2 split_module + | | 3 v +------------------------------------------------------------------------------------+ | +--------------------+ +---------------------+ +--------------------+ | | |Partition 1 | |Partition 2 | |Partition 3 | | | | | | | | | | | | Layer 1 | +---------> Layer 4 | | | | | | + | | | + | +-------> Layer 6 | | | | | | | | | | | | | | | | v | | | | | | | | | | | Layer 2 | | | | | | | | | | | + | | | v | | | | | | | | | | | Layer 5 +---------+ | | | | | v | | | | | | | | | Layer 3 +---------+ | | | | | | | | | | | | | | +---------+----------+ +---------+-----------+ +-----------+--------+ | | | | | | +------------------------------------------------------------------------------------+ | | | 4 | 4 | 4 | v v v +---------+----------+ +---------+-----------+ +----------+---------+ | | | | | | | Device 1 | | Device 2 | | Device 3 | | | | | | | +--------------------+ +---------------------+ +--------------------+ 至此,我们分析了自动平衡机制,下一篇我们看看如何切分数据和一些运行时机制。 0xFF 参考 https://docs.nvidia.com/cuda/cuda-runtime-api/stream-sync-behavior.html#stream-sync-behavior CUDA学习:基础知识小结 CUDA随笔之Stream的使用 NVIDIA解决方案架构师深度解析大规模参数语言模型Megatron-BERT Accelerating Wide & Deep Recommender Inference on GPUs HugeCTR: High-Performance Click-Through Rate Estimation Training https://discuss.pytorch.org/t/how-to-prefetch-data-when-processing-with-gpu/548 https://github.com/NVIDIA/apex/ https://github.com/justheuristic/prefetch_generator https://pytorch.org/tutorials/intermediate/model_parallel_turotial.html https://pytorch.org/docs/stable/autograd.html https://pytorch.org/docs/notes/cuda.html https://zhuanlan.zhihu.com/p/61765561 https://pytorch.apachen.org/docs/1.7/64.html https://zhidx.com/p/217999.html 原文链接:https://www.icode9.com/content-1-1151473.html
  13. 标签:阿格拉普算法(同国际组织)国际组织希尔排序 @测试 public void test013()引发异常{ //希尔排序,在插入排序算法的基础上做分组排序(先按一定步长排序) int k=0; int[] arr={4,3,5,3,6,2,8,1,9,3,5,1,2,3,6,8,0,8,7,6,5,4 }; int grap=arr。长度/2; while(grap 0){ 0 for(int j=grap;j arr.lengthj ) { for(int I=j;i=grapI-){ 0 如果 k; int temp=arr[I]; arr[I]=arr[I-grap]; 温度; } else { 打破; } } } grap=grap/2; } 系统。出去。println(数组。tostring(arr)); System.out.println('希尔排序算法次数:' k); } 原文链接:https://www.icode9.com/content-1-1151474.html
  14. 标签:的Java编程适合学习程序员软件英语。 这些年来,随着互联网的发展,程序员已经成为一个非常受欢迎的职业,越来越多的人开始在职业前景和薪酬方面涌入IT行业。那么Java开发适合学习谁呢? 如果你对学习编程感兴趣,它适合任何人。毕竟现在小学生都在学编程。如果你想成为一个职业,真的是一个适合不适合的问题。这里只讨论是否适合你这个职业,有四个现实的硬性指标可以参考。 1度的兴趣。 兴趣是基础。如果你对编码一点都不感兴趣,打字会让你觉得自己很大。作为程序员,至少不应该讨厌打字的感觉,这是最基本的。 2逻辑思维能力。 这是程序员最基本的要求。逻辑思维能力差的人不适合学习编程。当然,对于一般理工类学生来说,逻辑思维是没有问题的。对于大多数美术生来说,喜欢跟着感觉走,跳出思维的人一般不适合当程序员。 3英语 初级程序员英语要求不高,高中英语水平足够。如果你想发展一流的技术,提高你的英语能力是必要的。毕竟很多技术都来自美国。初中程序员阶段高中以上英语就够了。要想拔尖,至少要有四六级。如果英语不好,可以看看JAVA开发常用的英语词汇。 4数学 学习编程的好消息是“数学不重要,除非搞科研”。对于开发软件的企业来说,初高中的数学知识就足够了。当然,如果我们想发展人工智能和大数据,一些高等数学、线性代数、概率等知识还是会有很多用处的。 根据以上几点,可以判断自己是否适合学习Java开发。对于想学Java的人来说,最好的建议就是参加培训。培训的好处是专业的老师可以系统的教你。学什么知识,重点在哪里,都可以讲清楚。比如教师有十几年IT行业经验,教师有保障。 原文链接:https://www.icode9.com/content-1-1151481.html
  15. 标签:_ _ count函数pythonfunc修饰def。 引言 在软件开发中,当需要创建高度重复的代码时,有必要寻求一个优雅的解决方案。python中的元编程通过创建函数和类来修改、生成或打包现有代码,从而解决了这个问题。Decorator是一种用于在python中包装函数的机制。 内建装饰器 Python提供了一些内置的装饰器,比如@staticmethod、@classmethod和@property。 通常,装饰器内部的代码涉及创建一个新函数,接收带有*args和**kwargs的任意参数,调用函数内部的包装函数并返回包装函数的结果;最后,这个新创建的函数作为装饰器的结果返回。应该注意的是,装饰器通常不会修改包装函数的返回结果。 自定义装饰器 例1:统计函数执行时间 导入时间 从functools导入包装 #定义装饰函数:统计函数执行时间。 def timethis(func): @包装(func) def包装(*args,**kwargs): start=time.time() 结果=func(*args,**kwargs) end=time.time() 打印(功能。__名称_ _,结束-开始) 回送结果 返回包装 #使用装饰器。 @timethis def计数(n): 而n 0: n -=1 打印(n) if __name__=='__main__': 计数(5) 输出如下: 四 三 2 一个 0 计数0.0 例2:记录日志 导入时间 从functools导入包装 #定义装饰函数:记录。 def logthis(func): @包装(func) def包装(*args,**kwargs): 打印(功能。__name__,开始运行..) 结果=func(*args,**kwargs) 打印(功能。__name__,完成!!!') 回送结果 返回包装 #使用装饰器。 @logthis def计数(n): 而n 0: n -=1 打印(n) if __name__=='__main__': 计数(5) 输出如下: 计数开始运行… 四 三 2 一个 0 计数完毕! 例3:使用多个装饰器 如果我们对一个函数同时使用两个装饰器,会发生什么?或者用例1和2中定义的装饰器, #使用装饰器。 @logthis @timethis def计数(n): 而n 0: n -=1 打印(n) if __name__=='__main__': 计数(5) 输出如下: 计数开始运行… 四 三 2 一个 0 计数0.0 计数完毕! 如果我们在使用时改变装饰者的顺序,结果会不同。 #使用装饰者,在这里改变装饰者的顺序。 @timethis @logthis def计数(n): 而n 0: n -=1 打印(n) if __name__=='__main__': 计数(5) 输出结果如下: 计数开始运行… 四 三 2 一个 0 计数完毕! 计数0.0 如您所见,时间装饰器的输出位于最外层。可以理解为,装饰器是可以多层嵌套使用的。 原文链接:https://www.icode9.com/content-1-1151495.html

极客研究所

geekfooer.png.cea50058ddf1c8276491a8b29ec21205.png

GEEK.AC.CN 极客论坛,推动中国极客文化发展,建设全面网络科技体系,共同学习与成长。

Panel Title #2

痛苦本身可能是很多痛苦,但主要的原因是痛苦,但我给它时间像一些巨大的痛苦和痛苦一样陷入其中。为了让最少的人到来,除了从中获得目标之外,我们还可以进行任何劳动学派。

Home
Search
Existing user? Sign In
×
×
  • Create New...

Important Information

Dear visitors, please read and agree to our website rules before visiting our website!Guidelines