精华内容
下载资源
问答
  • 分布式计算任务调度算法总结

    万次阅读 2019-02-20 17:08:55
    主要有这些因素影响着分布式系统的性能:网络延迟、数据通信效能、计算节点处理能力、任务的分割、无法预算处理时间、任务的颠簸等等。 我们在寻求分布式计算调度算法时,就是有针对性的以解决这些问题为目的,从...

    一、影响分布式系统性能的因素
    主要有这些因素影响着分布式系统的性能:网络延迟、数据通信效能、计算节点处理能力、任务的分割、无法预算处理时间、任务的颠簸等等。
    我们在寻求分布式计算调度算法时,就是有针对性的以解决这些问题为目的,从各个角度,不同侧面,利用一种或者集中方法结合起来的形式,从而达到最优解,使得系统效率相对最高。

    二、几种基本的调度算法
    获得网络负载均衡有几个基本的方法。这些方法可以结合使用,形成更高级的方法。以下是几种基本方法:
    1、轮转法
    轮转算法是所有调度算法中最简单也最容易实现的一种方法。轮转法简单地在一串节点中线性轮转,平衡器将新请求发给节点表中的下一个节点,如此连续下去。
    这个算法在DNS域名轮询中被广泛使用。但是简单应用轮转法DNS转换,可能造成持续访问同一节点,从而干扰正常的网络负载平衡,使网络平衡系统无法高效工作。轮转法典型适用于集群中所有节点的处理能力和性能均相同的情况,在实际应用中,一般将它与其他简单方法联合使用时比较有效。
    2、加权法
    加权算法根据节点的优先级或权值来分配负载。权值是基于各节点能力的假设或估计值。加权方法只能与其他方法合用,是它们的一个很好的补充。
    3、散列法
    散列法也叫哈希法(Hash),通过单射不可逆的Hash函数,按照某种规则将网络请求发往集群节点。与简单加权法相似。
    4、最少连接法
    针对TCP连接进行在最少连接法中,管理节点纪录目前所有活跃连接,把下一个新的请求发给当前含有最少连接数的节点。
    缺陷是某些应用层会话要消耗更多的系统资源,尽管集群中连接数平衡了,但是处理量可能差别很大,连接数无法反映出真实的应用负载。
    5、最低缺失法
    在最低缺失法中,管理节点长期纪录到各节点的请求情况,把下个请求发给历史上处理请求最少的节点。与最少连接法不同的是,最低缺失记录过去的连接数而不是当前的连接数。
    6、最快响应法
    管理节点记录自身到每一个集群节点的网络响应时间,并将下一个到达的连接请求分配给响应时间最短的节点。在大多数基于LAN的集群中,最快响应算法工作的并不是很好,大多数与以太网连接的现代系统,有部分负载时,可在1ms或更短时间内响应,这使得这种方法没有意义。

    三、几种高级的调度算法
    1、基于遗传算法的分布式任务调度策略
    (1)符号串表示
    符号串表示必须能够唯一地表示搜索空间中的所有搜索节点。对于多处理器调度问题,一个有效的调度必须满足下列条件:
    (一)调度任务之间的先后关系;
    (二)完整性和唯一性条件(每一个任务都在调度中出现且出现一次)。监控任务表是满足这种条件的可行方法,每个表对应于一个处理器上运行的监控任务子集。一个调度的描述如下图所示。

    (2)起始群体
    在遗传算法的每一次迭代中,必须维持一个符号串的群体。起始的符号串群体通常是随机生成的。假定以下条件:在调度中每个处理器上的任务都按高度升序排列于表中。这样可以保证低高度的任务优先执行,保证算法的有效性。有时高度排序条件并不是充分条件,在此前提下,高度的定义被修正以减小这种情况出现的可能性。
    (3)适应度函数
    对于MSP,适应度函数需要考虑吞吐量、完成时间以及处理器的使用等因素。而对上面问题,适应度函数仅取决于调度的完成时间。一个调度完成时间定义为任务图中所有任务执行完成所用的时间,这个使时间最小的调度就是最优调度。
    (4)遗传操作
    (一)交叉
    如果交叉截点的选取使得每个交叉截点两侧任务的高度都是不同的,并且交叉截点前面最近任务的高度都是一样的,则新生成的符号串一定有效。
    (二)繁殖
    繁殖操作通过从旧的群体中选取适应度值最大的符号串来构成新的群体。选取的准则为:具有较高适应度值的符号串应有较多的机会在下一代中存活。这是因为“好的”的符号串具有较高的适应度值并应被保留到下一代。
    (三)突变
    对于MSP,突变由随机变换两个高度相同的任务来实现突变算法如下:
    1、随机选取一个任务T1;
    2、比较高度,找出符号串中高度相同的任务T2;
    3、交换任务,通过在调度中交换任务T1和T2来生成一个新的符号串;
    4、突变操作用突变概率来控制,这一算法在一个符号串上施用突变操作以生成一个新的符号串。
    (5)完整的遗传算法
    问题求解的完整遗传算法如下:
    Genetic-Algorithm()
    {
    调用调度生成算法N次并将生成的符号串存入Group;
    Do
    {
    计算Group中每个符号串的适应度值;
    调用繁殖算法;
    令BESTSIRING为Group中适应值最大的符号串;
    For(i=1 ; i<=GroupNum/2; i++)
    {
    从Group中取出两个符号串并以概率P¬1调用交叉操作;
    if(交叉操作发生)
    将生成的符号串加入Temp;
    else
    将原符号串加入Temp;
    }
    对Temp中的每一个符号串,以概率P2调用突变算法;
    if(突变操作发生)
    将新生成的符号串加入NewGroup;
    else
    将原符号串加入NewGroup;
    用BESTSIRNG取代Group中适应度值最小的符号串;
    }
    while(算法尚未满足收敛准则);
    }

    2、蚁群算法求解分布式系统任务分配问题
    

    (1)基本蚁群算法
    Dorigo 首先提出了蚁群算法。蚁群算法是模仿真实的蚁群行为而提出的一种模拟进化算法。蚂蚁个体之间是通过一种称之为信息素的物质进行信息传递,从而能相互协作,完成复杂的任务。蚂蚁在运动过程中,能够在它所经过的路径上留下信息素,而且蚂蚁在运动过程中能够感知这种物质的存在及其强度,并以此指导自己的运动方向,蚂蚁倾向于朝着该物质强度高的方向移动。因此,由大量蚂蚁组成的蚁群的集体行为便表现出一种信息正反馈现象:某一路径上走过的蚂蚁越多,则后来者选择该路径的概率就越大。蚂蚁个体之间就是通过这种信息素的交流,搜索到一条从蚁巢到食物源的可能的较短路径。
    蚁群算法的步骤可简要地表述为:①设置所有参数,信息素痕迹初始化;②每只蚂蚁根据状态转移规则建立一个完整的解,状态转移规则主要取决于信息素和启发式信息;③更新信息素。这3 个过程重复直到满足终止条件。
    (2)蚁群算法求解任务分配问题
    在用于任务分配问题求解的蚁群算法中,每一个人工蚂
    蚁是具有下述特性的智能体:
    当它选择把任务指派给处理器时,它会在连接, 上留下一种称为痕迹的物质(等价于信息素);
    它以一定的概率为一指定任务选择处理器,该概率为在连接, 上的启发式信息和的痕迹量的函数;
    在构造一个完整的解时,已经被处理的任务被加以禁止,所有分配到处理器的任务的KOP需求如果超过该处理器
    的能力限制,则该处理器也被禁止,如此重复直到所有的任务都已被处理器处理为止。
    该启发式算法使用由只蚂蚁组成的群体来一步步地进行解的构造。每只蚂蚁代表建立解的一个独立过程,这个过程分两步来完成:①蚂蚁选择将要被分配的任务;②对每个已经选择的任务,分配处理器执行它。若干蚂蚁过程之间通过信息素值来交换信息,合作求解并不断优化。所有任务均被处理器处理完意味着蚂蚁建立解过程的结束。
    3、一种自适应的分布式调度策略
    (1)总体结构图
    系统结构如图1所示,前端的共享介质型集线器(前端HUB)作为集群的单一人口点,借助于集线器的共享介质特征,使褥所有访间集群的数据包都能被内部节点的同构网卡接收并传往它们各自的数据链路层。集群内部所有服务节点都配备两块网卡,上端网卡绑定一个对外的公共IP地址(VIP),以实现集群的单一IP映像;每个下端网卡配备一个内部1P地址,负贵和集群管理控制台进行交互和发送数据包到外部网关。控制台节点机负贵管理和监视各个服务节点的工作状态。
    (2)调度原理
    该系统的调度原理如图2所示。其基本思想是在连接请求建立的初期,根据各个Server节点负载状况对TCP连接的第二次握手信号进行一定的延时,使得当前负载最轻的节点总是最

    先响应客户端的连接请求。通常根据连接请求内容和服务类型的不同,各个Server节点的工作负载往往表现出不均衡性,而且这种不均衡性是难以进免的,也是随时间不断变化的。本策略正是利用这种不均衡性,根据负载的动态变化来自动进行调度,在各个Server节点间达到一种自适应的平衡。而且不摇要节点间相互通讯,因此实现简单,开销更小。


    其工作原理描述如下:
    1)t1时刻,客户端访间集群服务器,给VIP发SYN请求,准备建立连接;
    2)SYN包到达前端HUB后,以广播的形式发送到各个Server节点的同构网卡;
    3)驻留在各个Server上的调度处理模块收到SYN请求后,根据自身的负载情况(由负载收集模块给出)对该请求进行一定的延时.负载重的Server节点延时大,负载轻的Server节点延时小,使得负载较轻的Server节点先发第二次握手信号(即SYN十ACK包)。如在LoadA<LoadB的情况下DelayA<DelayB,于是A的回应先发出;
    4)t2时刻,Load较小的ServerA节点的应答信息达到客户端,于是客户端给VIP发送第三次握手信号;
    5)t3时刻,Load较大的ServerB节点的应答信息到达客户端,由于t3>t2,客户端已经收到A的应答,TCP协议栈会自动丢弃B的应答;
    6)第三次握手信号经由前端HUB到达各个Server节点,包过滤模块检查该应答信号的ACK序列号是否与自身的初始序列号一致,即判断NACK和(NISN+1)是否相等,如果相等,则允许通过;否则向上层TCP协议栈发送一个RST数据包以撤销该连接。图示中ServerA将建立起连接,ServerB将撤销连接,从而选择当前负载较轻的ServerA为新来的请求服务。

    展开全文
  • Python-多任务总结

    千次阅读 2021-01-08 12:16:18
    任务任务什么是多任务任务理解线程线程介绍线程方法使用线程完成任务类的方式创建线程多线程共享全局变量线程同步进程进程定义进程概念进程与程序区别进程方法使用进程完成任务通过继承Process类创建进程...

    Python-多任务

    多任务

    什么是多任务

    • 多任务就是可以同时做多件事情就叫多任务

    多任务理解

    • 并发:CPU小于当前的执行的任务,是假的多线程
    • 并行:CPU大于当前的执行的任务,是真的多线程

    线程

    线程介绍

    • 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位
    • 导入:import threading

    线程方法

    • start: 启动线程
      • 该线程创建出来为主线程
      • 使用start会等子线程执行结束,然后主线程才结束
    • setDaemon : 守护线程
      • 不会等子线程执行结束
      • 当主线程结束就直接结束
    • join: 等待子线程执行结束
      • 放在start后使用
      • 当任务中存在多线程调用时,会先等子线程执行结束在去执行另外一个子线程
    • threading.enumerate : 查看创建的线程
      • 注意:主线程也算在内
      • 是在start之后创建出来的子线程

    使用线程完成多任务

    # -*- coding: utf-8 -*-
    # @Author: Small-J
    # @Date  : 2020/12/28
    
    """
    总结如下:
    线程是在start执行之后创建
    start : 启动线程
    target : 目标
    setDaemon : 守护线程
    join : 线程等待
    """
    import threading
    import time
    
    
    def sing():
        for i in range(5):
            print('我正在唱歌')
            time.sleep(1)
    
    
    def dance():    # 子线程
        for i in range(5):
            print('我正在跳舞')
            time.sleep(1)
    
    
    # def demo():
    #     print('Hello world')
    
    
    if __name__ == '__main__':
        # target: 目标,调用的对象,
        th = threading.Thread(target=sing)
        th1 = threading.Thread(target=dance)
        # print(threading.enumerate())
        # setDaemon: 守护线程,不会等子线程执行结束
        # th.setDaemon(True)
        # th1.setDaemon(True)
    
    
        # 启动线程
        th.start()  # 主线程,主线程会等到子线程执行结束
        th1.start()
        print(threading.enumerate())
        # 等待子线程执行结束
        # th.join()
        # th1.join()
    
        # 查看线程数量
        # 为什么结果却为3个呢
        # 因为还有一个主线程在内,存在两个子线程
        print(threading.enumerate())
    

    类的方式创建线程

    • 当想用类的方式创建的时候,需要在类中即成thread.Thread(看导入的情况而定)
    • 如果想进行传参的话,需要使用到super函数来而定
    • 当想在类中使用多线程,要在类中的run方法中实现
    • 通过start来启动多线程
    # -*- coding: utf-8 -*-
    # @Author: Small-J
    # @Date  : 2020/12/28
    
    import threading
    import time
    
    
    class Animal(threading.Thread):
        def run(self) -> None:
            print('this is animal')
            time.sleep(1)
    
    
    if __name__ == '__main__':
        for i in range(5):
            r = Animal()
            r.start()
    
        print(threading.enumerate())
    
    

    多线程共享全局变量

    • 线程之间可以共享全局变量,但是会出现计算出错。可通过锁来解决这么一系列问题
    • 互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性
    • 多线程之间传参可通过args来进行
    • mutex = threading.Lock() : 创建锁
    • mutex.acquire() : 上锁操作
    • mutex.release() : 解锁操作
    # -*- coding: utf-8 -*-
    # @Author: Small-J
    # @Date  : 2020/12/29
    
    
    import threading
    import time
    import dis
    num = 100
    
    """
    首先要知道,Python中的多线程并非真正的多线程,
    Python中的线程也并非采用单个执行来运行多线程,
    而Python中的多线程采用的是时间,轮转来执行,什么意思呢?
    意思就是当第一个线程执行的差不多了,然后把一个线程扔出去,让第一个线程等着,
    在把第二个线程继续执行,执行的差不多的时候,在把第二个线程扔出去,让第二个线程等着,
    所以这就出现了线程之间的共享全局变量的一个问题,如何解决这个问题呢。
    可以通过锁来解决这么一个问题的出现
    """
    
    import threading
    
    # 创建锁
    mutex = threading.Lock()
    
    
    def demo(nums):
        global num
        # 上锁操作
        mutex.acquire()
        for i in range(nums):
            num += 1
        # 解锁操作
        mutex.release()
        print(f'demo--{num}')
    
    
    def demo1(nums):
        global num
        # 上锁操作
        mutex.acquire()
        for i in range(nums):
            num += 1
        mutex.release()
        print(f'demo1--{num}')
    
    
    if __name__ == '__main__':
        # 多线程之间传参,可以通过args
        # 传入多参数要以元祖的形式
        t = threading.Thread(target=demo, args=(1000000, ))
        t1 = threading.Thread(target=demo1, args=(1000000,))
    
        t.start()
        t1.start()
        time.sleep(2)
        print(f'main--{num}')
    
    # def funt(a):
    #     a += 1
    #     print(a)
    #
    # print(dis.dis(funt))
    

    线程同步

    • 线程同步我们需要使用threading.Condition()完成线程同步
    # 线程同步
    cond = threading.Condition()
    
    # 等待
    cond.wait()
    
    # 唤醒
    cond.notify()
    

    实现效果:

    • 天猫精灵:小爱同学
    • 小爱同学:在
    • 天猫精灵:现在几点了?
    • 小爱同学:你猜猜现在几点了
    # -*- coding: utf-8 -*-
    # @Author: Small-J
    # @Date  : 2020/12/29
    
    import threading
    
    """
    天猫精灵:小爱同学
    小爱同学:在
    天猫精灵:现在几点了?
    小爱同学:你猜猜现在几点了
    """
    
    # 线程同步
    cond = threading.Condition()
    
    
    class Tmall(threading.Thread):
        def __init__(self, name):
            super(Tmall, self).__init__(name=name)
    
        def run(self) -> None:
            cond.acquire()  # 上锁操作
    
            print(f'{self.name} : 小爱同学')
            cond.wait()
            print(f'{self.name} : 现在几点了?')
            cond.notify()
            # 解锁操作
            cond.release()
    
    
    class love(threading.Thread):
        def __init__(self, name):
            super(love, self).__init__(name=name)
    
        def run(self) -> None:
            cond.acquire()
            print(f'{self.name} : 在')
            cond.notify()
            cond.wait()
            print(f'{self.name} : 你猜猜现在几点了')
    
            cond.release()
    
    
    if __name__ == '__main__':
    
        tianmao = Tmall(name='天猫精灵')
        xiaoai = love(name='小爱同学')
    
        tianmao.start()
        xiaoai.start()
    
    

    进程

    进程定义

    • 进程是计算机中的程序关于某数据集合上的一次运动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。并且进程是线程的容器。

    进程概念

    • 进程是一个实体。每一个进程都有它自己的地址空间。
    • 进程是一个执行中的程序
    • 进程是操作系统中最基本、重要的概念
    • 任务调度和执行的基本单位
    • 多个进程同时执行的顺序是随机的

    进程与程序区别

    • 进程:正在执行的程序。动态的,暂时的
    • 程序:没有执行的代码,是一个静态的,永久的

    进程方法

    • 通过multiprocessing.Process模块
    • group : 参数未使用,默认值为None
    • target : 表示调用对象,即子线程要执行的任务
    • args : 表示调用的位置参数元祖
    • kwargs : 表示调用对象的字典
    • name : 子进程名称

    使用进程完成多任务

    • 注意: 多线程同时执行的顺序是随机的
    # -*- coding: utf-8 -*-
    # @Author: Small-J
    # @Date  : 2020/12/31
    
    """
    总结如下:
    什么是多线程?
    多线程就是相当于就是操作系统运行多个任务,
    进程是线程的容器,一个进程最少要有一个线程
    进程是正在执行的程序,动态的
    """
    
    # 导入进程
    import multiprocessing
    # 当想导入进程队列时,需要从multiprocessing导入
    # 安全队列
    from multiprocessing import queues
    import time
    
    
    def test():
        while True:
            print('1')
            time.sleep(1)
    
    
    def test1():
        while True:
            print('2')
            time.sleep(1)
    
    
    def test2():
        while True:
            print('3')
            time.sleep(1)
    
    
    if __name__ == '__main__':
        # Process : 创建进程的方法
        t = multiprocessing.Process(target=test)
        t1 = multiprocessing.Process(target=test1)
        t2 = multiprocessing.Process(target=test2)
    
        # 启动多线程
        t.start()
        t1.start()
        t2.start()
    
        print(4)
    

    通过继承Process类创建进程

    class Demo(multiprocessing.Process):
        def run(self):
            while True:
                print("--1--")
                time.sleep(1)
                
                
    if __name__ == '__main__':
        p1 = Demo()
        p1.start()
    

    多进程共享全局变量

    • 注意⚠️: 多进程之间是不能进行共享全局变量的,但想通过共享全局变量,只能通过多进程的queue队列(注意:这里说的是多进程的队列,而不是普通的队列)
    # -*- coding: utf-8 -*-
    # @Author: Small-J
    # @Date  : 2021/1/5
    
    import multiprocessing
    import time
    
    """
    总结如下:
    多进程之间是不能进行共享全局变量的
    """
    num = 100
    
    
    def num1():
        global num
        num += 100
        print(f'num1 for {num}')    # 200
    
    
    def num2():
        print(f'num2 for {num}')    # 100
    
    
    if __name__ == '__main__':
        m1 = multiprocessing.Process(target=num1)
        m2 = multiprocessing.Process(target=num2)
    
        # 启动多进程
        m1.start()
        m2.start()
    

    多进程共享全局变量+多进程队列

    • 使用多进程队列可以解决这么个问题
    • q = multiprocessing.Queue() : 创建进程队列
    • q.get() : 从队列取值
    • q.put(): 将一个数据存入队列
    • q.qsize() : 返回队列的大小
    • q.empty() : 判断队列是否为空
    • q.full() : 判断队列是否满了
    # -*- coding: utf-8 -*-
    # @Author: Small-J
    # @Date  : 2021/1/5
    
    import multiprocessing
    import time
    
    """
    总结如下:
    多进程之间是不能进行共享全局变量的
    当想进行共享全局变量
    """
    num = 100
    
    
    def num1(q):
        global num
        num += 100
        q.put(num)  # 200
        print(f'num1 for {num}')    # 200
    
    
    def num2(q):
        # print(f'num2 for {num}')    # 100
        data = q.get()  # 200
        print(f'num2 for {data}')
    
    
    if __name__ == '__main__':
        q = multiprocessing.Queue()
        m1 = multiprocessing.Process(target=num1, args=(q, ))
        m2 = multiprocessing.Process(target=num2, args=(q, ))
    
        # 启动多进程
        m1.start()
        m2.start()
    

    进程池

    • 当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程,但是如果是上百甚至上千个目标,手动的去创建的进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法,也就是进程池。
    • apply_asyn : 非阻塞,异步形式
    • close : 关闭进程池,使其不在接受新的任务
    • join : 主进程阻塞,等待子进程的退出,join方法要在close后使用
    • 进程池之间通信使用的是:q = multiprocessing.Manager().Queue()
    # -*- coding: utf-8 -*-
    # @Author: Small-J
    # @Date  : 2021/1/5
    
    
    import multiprocessing
    
    
    def demo1(q):
        q.put("a")
    
    
    def demo2(q):
        data = q.get("a")
        print(data)
    
    
    if __name__ == '__main__':
        # 当使用进程池的时候并不能使用进程使用的队列
        # 需要使用到进程池的队列
        q = multiprocessing.Manager().Queue()
    
        # 创建进程池
        po = multiprocessing.Pool(2)
    
        # 异步启动,非阻塞
        po.apply_async(demo1, args=(q, ))
        po.apply_async(demo2, args=(q, ))
    
        # 同步启动,阻塞
        # po.apply()
    
        # 关闭进程池
        po.close()
    
        # 等待子进程池结束
        # 调用join之前,先调用close函数,否则会出错
        # 执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
        po.join()
    
    

    多任务文件夹复制

    # -*- coding: utf-8 -*-
    # @Author: Small-J
    # @Date  : 2021/1/5
    
    """
    获取用户要复制的文件夹名字
    创建一个新的文件夹
    获取文件夹所有待拷贝的文件名字
    创建进程池
    添加拷贝任务
    """
    
    import multiprocessing
    import os
    import time
    
    
    def copy_file(old_folder_name, new_folder_name, file_name, q):
        """复制文件内容"""
        with open(old_folder_name + '/' + file_name, 'rb')as f:
            content = f.read()
    
        with open(new_folder_name + '/' + file_name, 'wb')as f1:
            f1.write(content)
    
    
    def main():
        # 获取用户要复制的文件夹名字
        old_folder_name = input('请输入你要创建的文件夹名字:')
    
        # 创建一个新的文件夹
        new_folder_name = old_folder_name + '[附件]'
    
        if not os.path.exists(new_folder_name):
            os.mkdir(new_folder_name)
    
        # 获取文件夹中所有的内容名字
        file_name_list = os.listdir(old_folder_name)
    
        # 创建进程池队列
        q = multiprocessing.Manager().Queue()
    
        # 创建进程池
        po = multiprocessing.Pool(2)
    
        # 添加拷贝任务
        for file_name in file_name_list:
            po.apply_async(copy_file, args=(old_folder_name, new_folder_name, file_name, q))
    
        # 关闭进程池
        po.close()
    
        # 等待子进程执行结束
        po.join()
    
        all_file_num = len(file_name_list)
        copy_ok_num = 1
        while True:
            copy_ok_num += 1
            time.sleep(1)
            print("\r复制的进度为:%.2f %%" % (copy_ok_num * 100 / all_file_num), end='')
    
            if copy_ok_num >= all_file_num:
                break
    
    
    if __name__ == '__main__':
        main()
    

    总结如下:

    • 也存在着线程池和进程池
    • IO密集型适用于多线程
    • 计算密集型适用多进程
    • 多线程无法利用多核CPU发挥优势,会轮流使用CPU来发挥优势
    • 多进程是利用多核CPU发挥优势

    进程与线程区别

    • 根本区别
      • 进程: 操作系统资源分配的基本单位
      • 线程: 任务调用和执行的基本单位
    • 开销
      • 进程: 通过复制代码+资源创建子进程,每个进程都有独立的代码和数据空间,程序之间的切换会有较大的开销
      • 线程: 在同一份代码里,创建线程,共享内存,开销较小。
    • 分配内存
      • 进程:系统在运行的时候为每个进程分配不同的内存空间
      • 线程: 线程所使用的资源是它所属的进程的资源
    • 包含关系
      • 进程: 一个进程可以拥有多个线程
      • 线程: 线程是进程的一部分

    协程

    • 协程,又称为微线程, 它是实现多任务的另一种方式,只不过是比现场更小的执行单位。因为它自带CPU的上下文,这样只要在合适的时机,我们就可以把一个协程切换到另一个协程。

    协程与线程差异

    • 线程:每个线程都有自己换成Cache等等数据,操作系统还会做这些数据的恢复操作,所以线程的切换非常消耗性能
    • 协程:单纯的操作CPU的上下文,所以一秒切换上百万次系统都能扛住。说哟完成多任务效率比线程和进程都高
    # 使用yield来实现协程
    import time
    
    
    def task():
        while True:
            print('--1--')
            time.sleep(1)
            yield 'task'
    
    
    def task1():
        while True:
            print('--2--')
            time.sleep(1)
            yield 'task2'
    
    
    def main():
        t1 = task()
        t2 = task1()
    
        # print(t2)     # 对象地址值
        # print(t1)
    
        # next(t1)
        # next(t2)
        while True:
            next(t1)
            next(t2)
    
    
    if __name__ == '__main__':
        main()
    
    

    greenlet的使用

    • greenlet也是实现协程的一种方式,但是它实现协程并不会手动的去切换,还需要手动的实现。
    • 安装:pip install greenlet
    # -*- coding: utf-8 -*-
    # Author : Small-J
    # 2021/1/8 11:44 上午
    
    from greenlet import greenlet
    import time
    
    
    def demo1():
        while True:
            print("demo1")
            gr2.switch()
            time.sleep(0.5)
    
    
    def demo2():
        while True:
            print("demo2")
            # 通过switch到其他协程地方,该协程会被挂起
            gr1.switch()
            time.sleep(0.5)
    
    
    if __name__ == '__main__':
        gr1 = greenlet(demo1)
        gr2 = greenlet(demo2)
    
        gr1.switch()
    

    gevent的使用

    • 比greenlet更强大的并且能够主动切换任务的模块gevent
    • 安装: pip install gevent
    • 原理:当一个greenlet遇到IO操作时,比如访问网络,就主动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行
    • 由于IO操作非常耗时,经常使程序处于等待状态,有了genvet为我们自动切换协程,就保证总有greenlet在运行
    • 在爬取图片等操作的时候适用于领域
    # -*- coding: utf-8 -*-
    # Author : Small-J
    # 2021/1/8 11:55 上午
    
    import gevent
    import time
    
    # 破解time.sleep延迟补丁,除了time,各种延迟都适配
    from gevent import monkey
    
    # 补丁
    monkey.patch_all()
    """
    总结如下:
    如果使用time.sleep就会变成同步的形式去执行,先是f1执行完之后在执行
        但如果想time.sleep被识别的话,就需要导入monkey模块
        
        
    需要使用gevent.sleep来实现,为什么需要呢?
        因为需要一种延迟的效果来展示,如网络请求延迟也可以
    """
    
    
    def f1(n):
        for i in range(n):
            print(gevent.getcurrent(), i)
            # gevent.sleep(0.5)
            time.sleep(0.5)
    
    
    def f2(n):
        for i in range(n):
            print(gevent.getcurrent(), i)
            # gevent.sleep(0.5)
            time.sleep(0.5)
    
    
    if __name__ == '__main__':
        # 创建协程
        # g1 = gevent.spawn(f1, 5)
        # g2 = gevent.spawn(f2, 5)
        # g1.join()
        # g2.join()
    
        # 简化上面的操作
        # greenlets, timeout=None, raise_error=False, count=None
        gevent.joinall([
            gevent.spawn(f1, 5),
            gevent.spawn(f2, 5)
        ])
    

    最后总结

    • 进程是资源分配的单位
    • 线程是操作系统调度的单位
    • 进程切换需要的资源最大,效率很低
    • 线程切换需要的资源一般,效率一般
    • 协程切换任务资源很小,效率高
    • 多线程、多进程根据CPU核数不一样可能是并行的,但是协程是在一个线程中,所以是并发
    展开全文
  • 定时任务实现方法总结与分析

    千次阅读 2016-06-01 17:37:40
    这次打算把我之前总结的一些东西陆陆续续的在博客里,希望下次用到的时候能够快速记起来~—————————————华丽的分割线——————————————–1 定时任务简介在应用开发中,经常需要一些周期性...

    上次熟悉了MarkDown的用法之后,由于各种原因一直没有时间更新博客。。。这次打算把我之前总结的一些东西陆陆续续的写在博客里,希望下次用到的时候能够快速记起来~

    —————————————华丽的分割线——————————————–

    1 定时任务简介

    在应用开发中,经常需要一些周期性的操作,如:需要在每天凌晨时候分析一次前一天的日志信息、需要每隔5分钟检查一下某个模块是否有异常然后自动发送邮件给管理员,在项目运行到第30天的时候需要执行某些操作等等。这些功能需求就需要我们使用一些定时任务方法去实现,本文将介绍目前J2EE项目常用的几种定时任务方法并比较它们的优缺点。

    2 J2EE项目中常用到的三种定时任务实现方法

    2.1 java.util.Timer类

    2.1.1 简介

    先来介绍下Java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。TimerTask类用于实现由Timer安排的一次或重复执行的某个任务。每一个Timer对象对应的是一个线程,因此计时器所执行的任务应该迅速完成,否则会延迟后续的任务执行。

    java.util.Timer类方法摘要

    void cancel()
    终止此计时器,丢弃所有当前已安排的任务。
    int purge()
    从此计时器的任务队列中移除所有已取消的任务。
    void schedule(TimerTask task, Date time)
    安排在指定的时间执行指定的任务。
    void schedule(TimerTask task, Date firstTime, long period)
    安排指定的任务在指定的时间开始进行重复的固定延迟执行。
    void schedule(TimerTask task, long delay)
    安排在指定延迟后执行指定的任务。
    void schedule(TimerTask task, long delay, long period)
    安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。
    void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
    安排指定的任务在指定的时间开始进行重复的固定速率执行。
    void scheduleAtFixedRate(TimerTask task, long delay, long period)
    安排指定的任务在指定的延迟后开始进行重复的固定速率执行。

    TimerTask类方法摘要

    boolean cancel()
    取消此计时器任务。
    abstract void run()
    此计时器任务要执行的操作。
    long scheduledExecutionTime()
    返回此任务最近实际执行的安排执行时间。

    2.1.2 使用方法

    使用Timer类的schedule(TimerTask task, long delay, long period)方法启动定时器。

        Timer  timer=new Timer();  
        MyTask myTask=new MyTask();  
        timer.schedule(myTask, 1000, 2000); 
    

    TimerTask类主要实现run()方法里的业务逻辑,用法如下:

    import java.text.SimpleDateFormat;  
    import java.util.Date;  
    import java.util.TimerTask;  
    
    public class MyTask extends TimerTask {  
    
        @Override  
        public void run() {  
            // TODO Auto-generated method stub  
            SimpleDateFormat simpleDateFormat=null;  
            simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");  
            System.out.println("当前的系统时间为:"+simpleDateFormat.format(new Date()));  
    
        }  
    }
    

    2.1.3 扩展内容(往定时任务方法中传参数)

    import java.text.SimpleDateFormat;  
    import java.util.Date;  
    import java.util.TimerTask;  
    
    public class WaitListTimerTask extends TimerTask {  
    private WaitList waitList;
    public WaitListTimerTask(WaitList waitList){
            this.waitList=waitList;
    }
    
        @Override  
        public void run() {  
            // 参数waitList使用示例  
            List<CourseWaitList> allCourseWaitList = this.waitList.getAllCourseWaitList();
    
        }  
    }
    

    2.2 Spring3.0以后自带的Spring-task

    2.2.1 简介

    Spring3.0以后自主开发了定时任务工具spring task,可以将它比作一个轻量级的Quartz,而且使用起来很简单,除spring相关的包外不需要额外的包,而且支持注解和配置文件两种形式,下面将分别介绍这两种方式。

    2.2.2 使用方法

    第一种:配置文件方式
    ①编写作业类
    即普通的pojo,如下:

    import org.springframework.stereotype.Service;  
    
    @Service  
    public class TaskJob {  
        public void job1() {  
    
            System.out.println(“任务进行中。。。”);  
    
        }  
    }
    

    ②在spring配置文件头中添加命名空间及描述

    <beans xmlns="http://www.springframework.org/schema/beans"  
    
        xmlns:task="http://www.springframework.org/schema/task"   
    
        …… 
    
        xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
    

    ③Spring配置文件中设置具体的任务

    <task:scheduled-tasks>   
    
            <task:scheduled ref="taskJob" method="job1" cron="0 * * * * ?"/>   
    
    </task:scheduled-tasks>  
    <context:component-scan base-package=" com.gy.mytask " />
    

    说明:ref参数指定的即任务类,method指定的即需要运行的方法,croncronExpression表达式,具体写法这里不介绍了,详情见附录。
    这个配置根据项目实际情况调整包的位置,spring扫描注解用的。

    第二种:使用注解形式
    ①编写作业类
    即普通的pojo,如下:

    import org.springframework.scheduling.annotation.Scheduled;    
    import org.springframework.stereotype.Component;  
    
    
    
    @Component(“taskJob”)  
    
    public class TaskJob {  
    
        @Scheduled(cron = "0 0 3 * * ?")  
    
        public void job1() {  
    
            System.out.println(“任务进行中。。。”);  
    
        }  
    
    }  
    

    注意:此处@Schedule注解有三个方法或者叫参数,分别表示的意思是:
    cron:指定cron表达式
    fixedDelay:即表示从上一个任务完成开始到下一个任务开始的间隔,单位是毫秒。
    fixedRate:即从上一个任务开始到下一个任务开始的间隔,单位是毫秒。

    ②添加task相关的配置

    <?xml version="1.0" encoding="UTF-8"?>  
    <beans xmlns="http://www.springframework.org/schema/beans"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"  
        xmlns:context="http://www.springframework.org/schema/context"  
        xmlns:tx="http://www.springframework.org/schema/tx"  
        xmlns:task="http://www.springframework.org/schema/task"  
        xsi:schemaLocation="  
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
            http://www.springframework.org/schema/context   
    http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd  
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
            http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"  
        default-lazy-init="false">  
    
        <context:annotation-config />  
    
        <!—spring扫描注解的配置   -->  
    <context:component-scan base-package="com.gy.mytask" />  
    
    <!—开启这个配置,spring才能识别@Scheduled注解   -->  
        <task:annotation-driven scheduler="qbScheduler" mode="proxy"/>  
    
        <task:scheduler id="qbScheduler" pool-size="10"/>  
    

    说明:理论上只需要加上《task:annotation-driven /》这句配置就可以了,这些参数都不是必须的。

    2.3 定时任务框架Quartz

    2.3.1 简介

    Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。Quartz的最新版本为Quartz 2.2.2。下面将介绍两种Quartz使用方式:

    2.3.2 使用方法

    第一种:作业类继承自特定的基类
    org.springframework.scheduling.quartz.QuartzJobBean
    ①编写作业类

    import org.quartz.JobExecutionContext;  
    import org.quartz.JobExecutionException;  
    import org.springframework.scheduling.quartz.QuartzJobBean;  
    public class Job1 extends QuartzJobBean {  
    
        private int timeout;  
        private static int i = 0;  
        //调度工厂实例化后,经过timeout时间开始执行调度  
        public void setTimeout(int timeout) {  
            this.timeout = timeout;  
        }  
    
        /** 
        * 要调度的具体任务 
        */  
        @Override  
        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {  
              System.out.println("定时任务执行中…");  
        }  
    }
    

    ②spring配置文件中配置作业类JobDetailBean

    <bean name="job1" class="org.springframework.scheduling.quartz.JobDetailBean">  
        <property name="jobClass" value="com.gy.Job1" />  
        <property name="jobDataAsMap">  
            <map>  
                <entry key="timeout" value="0" />  
            </map>  
        </property>  
    </bean> 
    

    说明:org.springframework.scheduling.quartz.JobDetailBean有两个属性,jobClass属性即我们在java代码中定义的任务类,jobDataAsMap属性即该任务类中需要注入的属性值。

    ③配置作业调度的触发方式(触发器)
    Quartz的作业触发器有两种,分别是
    org.springframework.scheduling.quartz.SimpleTriggerBean
    org.springframework.scheduling.quartz.CronTriggerBean
    第一种SimpleTriggerBean,只支持按照一定频度调用任务,如每隔30分钟运行一次。
    配置方式如下:

    <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">  
        <property name="jobDetail" ref="job1" />  
        <!-- 调度工厂实例化后,经过0秒开始执行调度 -->
        <property name="startDelay" value="0" />  
        <!-- 每2秒调度一次 --> 
        <property name="repeatInterval" value="2000" /> 
    </bean> 
    

    第二种CronTriggerBean,支持到指定时间运行一次,如每天12:00运行一次等。
    配置方式如下:

    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">  
        <property name="jobDetail" ref="job1" />  
        <!—每天12:00运行一次 -->  
        <property name="cronExpression" value="0 0 12 * * ?" />  
    </bean> 
    

    ④配置调度工厂

    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
        <property name="triggers">  
            <list>  
                <ref bean="cronTrigger" />  
            </list>  
        </property>  
    </bean> 
    

    说明:该参数指定的就是之前配置的触发器的名字。

    ⑤启动你的应用即可,即将工程部署至tomcat或其他容器。

    第二种:作业类不继承特定基类(推荐使用)
    Spring能够支持这种方式,归功于两个类:
    org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean
    org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean
    这两个类分别对应spring支持的两种实现任务调度的方式,即前文提到到java自带的timer task方式和Quartz方式。这里我只写MethodInvokingJobDetailFactoryBean的用法,使用该类的好处是,我们的任务类不再需要继承自任何类,而是普通的pojo。
    ①编写作业类(普通POJO)

    public class Job2 {  
    public void doJob2() {  
        System.out.println("不继承QuartzJobBean方式-调度进行中...");  
        }  
    }  
    

    ②配置作业类

    <bean id="job2"  
    class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
        <property name="targetObject">  
            <bean class="com.gy.Job2" />  
        </property>  
        <property name="targetMethod" value="doJob2" />  
        <!-- 作业不并发调度 -->  
        <property name="concurrent" value="false" />
    </bean> 
    

    说明:这一步是关键步骤,声明一个MethodInvokingJobDetailFactoryBean,有两个关键属性:targetObject指定任务类,targetMethod指定运行的方法。

    ③配置作业调度的触发方式(触发器)
    Quartz的作业触发器有两种,分别是
    org.springframework.scheduling.quartz.SimpleTriggerBean
    org.springframework.scheduling.quartz.CronTriggerBean
    第一种SimpleTriggerBean,只支持按照一定频度调用任务,如每隔30分钟运行一次。
    配置方式如下:

    <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">  
        <property name="jobDetail" ref="job1" />
        <!-- 调度工厂实例化后,经过0秒开始执行调度 -->    
        <property name="startDelay" value="0" />
        <!-- 每2秒调度一次 -->  
        <property name="repeatInterval" value="2000" />
    </bean> 
    

    第二种CronTriggerBean,支持到指定时间运行一次,如每天12:00运行一次等。
    配置方式如下:

    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">  
        <property name="jobDetail" ref="job1" />  
        <!—每天12:00运行一次 -->  
        <property name="cronExpression" value="0 0 12 * * ?" />  
    </bean> 
    

    ④配置调度工厂

    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
        <property name="triggers">  
            <list>  
                <ref bean="cronTrigger" />  
            </list>  
        </property>  
    </bean>
    

    说明:该参数指定的就是之前配置的触发器的名字。
    ⑤启动你的应用即可,即将工程部署至tomcat或其他容器。

    2.2.3 扩展内容

    ①配置多个定时器任务

    <util:properties id="applicationProps" location="classpath:enroll.properties" />
        <context:property-placeholder properties-ref="applicationProps" />
    
        <bean id="waitListExpireJob" class="com.yunteng.ngtl.enroll.tool.waitListExpireTaskJob" />
        <bean id="expireJobTask"
            class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
            <property name="targetObject" ref="waitListExpireJob" />
            <property name="targetMethod" value="expireProcess" />
        </bean>
        <bean id="waitListExpireTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
            <property name="name" value="waitListExpireTriggerName" />
            <property name="group" value="waitListExpireTriggerGroup" />
            <property name="jobDetail">
                <ref bean="expireJobTask" />
            </property>
            <property name="cronExpression">
                <value>#{applicationProps['cron.expireTask']}</value>
            </property>
        </bean>
    
        <bean id="waitListObserveJob" class="com.yunteng.ngtl.enroll.tool.waitListObserveTaskJob" />
        <bean id="observeJobTask"
            class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
            <property name="targetObject" ref="waitListObserveJob" />
            <property name="targetMethod" value="observeProcess" />
        </bean>
        <bean id="waitListObserveTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
            <property name="name" value="waitListObserveTriggerName" />
            <property name="group" value="waitListObserveTriggerGroup" />
            <property name="jobDetail">
                <ref bean="observeJobTask" />
            </property>
            <property name="cronExpression">
                <value>#{applicationProps['cron.observeTask']}</value>
            </property>
        </bean>
    
        <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
           <property name="triggers">
             <list>
               <ref local="waitListExpireTrigger" />
               <ref local="waitListObserveTrigger" />
             </list>
           </property>
         </bean>
    

    ②动态修改定时器任务调度时间周期(关键字:quartz change cron expression runtime)
    注意:目前只在Quartz1.8.6版本下测试成功

    CronTrigger cronTrigger = (CronTrigger) stdScheduler.getTrigger(triggerName,triggerGroupName);
    cronTrigger.setCronExpression(newCronExpression);
    stdScheduler.rescheduleJob(triggerName,triggerGroupName,cronTrigger);
    

    3 总结与分析

    ~ Timer Spring-Task Quartz
    作业类的继承方式 java.util.Timer中需要继承自java.util.TimerTask 普通的java类,不需要继承其他类 继承自org.springframework.scheduling.quartz.QuartzJobBean
    是否可以使用Cron表达式 不可以 可以 可以
    动态改变执行时间周期 可以,但是使用不灵活 资料太少,未找到相关方法 可以,将触发器重新启动即可重新调度任务(资料较多,目前只在Quartz1.8.6版本测试通过)
    使用难易程度 简单 简单 稍难,需要配置的部分相对较多且繁琐

    这篇博文到这里就结束了,希望下次有时间可以多添加一些图表等更加形象的内容。

    展开全文
  • 多类别动物图片分类任务(中) 在前半部分,我们已经完成了前两大步,并决定使用ResNet50预训练网络来训练模型。那么接下来,就让我们引入keras中已经封装好的ResNet50预训练网络参数。 ResNet50的引入 代码...

    多类别动物图片分类任务(中)

    在前半部分,我们已经完成了前两大步,并决定使用ResNet50预训练网络来训练模型。那么接下来,就让我们引入keras中已经封装好的ResNet50预训练网络参数。

    1. ResNet50的引入
      代码实现
    base_model = ResNet50(weights='imagenet',
                          include_top=False,
                          input_shape=(img_width, img_height, 3))
    base_model.trainable = False
    
    x = base_model.output
    x = GlobalAveragePooling2D(name='average_pool')(x)
    predictions = Dense(class_num, activation='softmax')(x)
    
    model = Model(inputs=base_model.input, outputs=predictions)
    model.compile(loss='categorical_crossentropy',
                  optimizer=optimizers.RMSprop(lr=1e-3),
                  metrics=['acc'])
    

    keras中已经封装好了resnet50的网络结构和预训练参数,可以通过

    from keras.applications.resnet50 import ResNet50
    

    来引入,并使用以上的代码进行实例化。
    那么,让我们赶紧来跑一下吧。
    最终,我们得到的验证集准确率为77.59%
    这的确是非常大的提升,而且由于ResNet更加的轻量化,因此其训练速度更快。
    可惜的是,依旧没有达到一期的任务期望。

    1. 一些简单的调整
      在上一篇博客中,我们讲到在初步引入预训练网络后,我们可以先调节一些参数,来尽可能地提高val_acc,经过几次简单的实验,我们最终选择学习率lr=5e-4。
    optimizer=optimizers.RMSprop(lr=5e-4)
    

    此时,我们得到的验证集准确率为79.35%

    1. 对数据进行预处理
      在上一篇博客中,我们说过数据存在两大问题:分配不均以及大小不等,其中前者可以使用过采样和欠采样得到一定程度上的解决,而后者,我们可以在让数据进入模型前,率先经过一步resize,这样可以大大的提高整个模型的训练速度。
      但是,需要注意的是,如果train和valication中的数据是经过resize再开始训练模型的话,最后的test中的数据也需要同样的处理,否则将会严重影响测试准确率。
      初次之外,我们通过观察会发现,原始数据中有近3成的图片存在边框,为了提高准确率,我们可以将其crop掉。
      代码实现
    # 这个文档用来进行去除图片的边界,以及调整大小到356*356
    
    import os
    import cv2
    from PIL import Image
    import warnings
    from PIL import ImageFile
    ImageFile.LOAD_TRUNCATED_IMAGES = True
    warnings.simplefilter("ignore", category=FutureWarning)
    Image.MAX_IMAGE_PIXELS = None
    
    base_path = "input"
    image_path = os.path.join(base_path, 'image')
    train_path = os.path.join(base_path, 'train')
    
    
    def removeBorder(fileDir):
        pathDir = os.listdir(fileDir)  # 取图片的原始路径
        for imgName in pathDir:
            img = cv2.imread(fileDir + '/' + imgName)
            imgtemp = cv2.resize(img, (456, 456))
            cropped = imgtemp[50:406, 50:406]
            cv2.imwrite(fileDir + '/' + imgName, cropped)
    
    
    if __name__ == '__main__':
        count = 0
        for i in range(1, 1505):
            fileDir = os.path.join(image_path, str(i))
            if os.path.isdir(fileDir):
                removeBorder(fileDir)
                count += 1
                print("processed :" + str(i) + ",and " + str(count) + "/168")
    
    

    此时,我们得到的验证集准确率为83.57%,距离一期的任务期望仅一步之遥!

    1. 数据增强
      提到图片分类,我们不可避免的需要采取一定的措施来应对数据量过小的困境,数据增强就是我们经常使用的好帮手。
      代码实现
    
    def preprocess(image):
        mean = [R_MEAN, G_MEAN, B_MEAN]
        image[..., 0] -= mean[0]
        image[..., 1] -= mean[1]
        image[..., 2] -= mean[2]
        return image
    
    
    # 可以对训练集进行数据增强处理
    train_datagen = ImageDataGenerator(preprocessing_function=preprocess,
                                       rotation_range=20,
                                       width_shift_range=0.1,
                                       height_shift_range=0.1,
                                       zoom_range=0.1,
                                       horizontal_flip=True,
                                       fill_mode='constant'
                                       )
    
    # 测试集不许动,去均值中心化完了之后不许动
    validation_datagen = ImageDataGenerator(preprocessing_function=preprocess)
    

    最后,我们得到的验证集准确率为84.31%,虽然增加的不多,但是终于完成了一期的任务!

    下一期,我会讲一下如何利用模型微调的方式完成二期任务。

    有什么不明白的参数,可以查看keras官方中文文档
    https://keras-cn.readthedocs.io/en/latest/other/application/

    展开全文
  • 1. 事件轮询(Event Loop) js实现异步的具体解决方案 什么叫轮询? 2. 宏任务和微任务 ...总结 这篇博文仅为个人理解,文章内提供一些更加权威的参考,如有片面及错误,欢迎指正 1. 事件轮询...
  • 学生宿舍管理系统 完成总结

    万次阅读 2016-03-04 11:14:52
    任务:通过此系统可以实现如下功能: 录入: 可以录入宿舍情况,包括宿舍号、可容纳学生数、已容纳学生数、男生/女生宿舍等信息; 可以录入学生住宿情况,包括学号、姓名、性别、宿舍号等信息。 其他信息可以自行...
  • 手写并发任务执行框架

    万次阅读 2020-04-03 22:06:41
    一、框架开发1、添加依赖2、任务处理器接口3、超时机制包装类4、任务信息类5、任务结果类型6、任务结果类7、框架主体处理类8、过期任务清理类二、框架测试1、自定义工作任务2、测试3、测试结果分析三、总结流程图...
  • CentOS 7+ 定时任务总结

    千次阅读 2017-09-07 20:09:31
    至此,总结通过 Linux 命令(crontab)实现定时任务的功能, 如果后续有其他发现,则进一步补充完善. crontab crontab 命令常用于Unix 和类 Unix的操作系统之中,用于设置周期性被执行的指令. 百度百科. Cron ...
  • Future模式可以理解成:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。 Future提供了三种功能: 判断任务是否完成 ...
  • Linux系统中 任务、进程和线程总结

    千次阅读 2014-05-09 16:03:52
    任务、进程和线程基本概念 多任务处理是指用户可以在同一时间内运行多个应用程序,每个正在执行的应用程序被称为一个任务。Linux 就是一个支持多任务的操作系统(Windows也是多任务操作系统),比起单任务系统它的...
  • 转载地址 :http://mp.weixin.qq.com/s/6SMOJWFcejil4MAsnBJWYQ、在工作中,你可能有各种各样的事情,这件事情忙完就... 工作计划制定完成 让上级了解计划的基本内容,执行流程与预期效果。获得反馈之后再根据领...
  • 实体关系抽取任务方法及SOTA模型总结

    千次阅读 多人点赞 2020-05-31 21:02:08
    实体关系抽取是从文本中的句子里抽取出一对实体并给出实体间关系的任务。该任务的输入是一句话,输出是一个spo三元组(subject-predicate-object)。 对于实体关系抽取任务,最容易想到的方法就是先抽取句子中的实体...
  • Spring 调度任务@scheduled学习总结

    千次阅读 2015-05-22 16:50:45
    官网Api:...1.initialDelay :初次执行任务之前需要等待的时间 @Scheduled(initialDelay =50
  • 故障转移和负载均衡之任务总结

    千次阅读 2014-12-19 22:06:47
    任务总结 任务结果1:  并没有实现真正意义上的故障转移,因为存储也是一个瓶颈,如果存储出现问题,那故障转移仍将回复到没有故障转移的情况。真正意义上的故障是不会在存储阶段没有高可用性。 结果1疑问:  ...
  • 我的2018:用一年的时间一份年终总结

    万次阅读 多人点赞 2018-12-30 23:01:37
    1、题记 不觉中,已经连续写了五年年终总结...得到罗胖提出跨年演讲要做20年,那我索性年终总结写一辈子吧。 差不多从读初中开始,每年都会写一些当下的思考和总结性的文章,从开始的流水账式的记录,到大学阶段的...
  • android 第一个项目完成! 阶段总结

    千次阅读 多人点赞 2016-01-06 17:54:31
    导师在思路上会给一些指导,但是技术上除了学长以前的半成品的代码之外,并无参考。 由于项目并没有开源,所以我在此也无法附上源码了。不过主要的一些android端功能的实现,我已经成文章分散在了我
  • 现在需求要定时地执行kettle的作业任务,自动化地完成ETL工作,kettle运行环境可分为Linux和Windows。 解决方案:(Windows) 1.1:安装jdk,下载kettle,kettle运行依赖jdk。 jdk下载地址:...
  • 程序员的十大任务,你完成了吗?

    千次阅读 2014-12-15 10:19:47
    但是,作为程序员,你知道你必须完成的十大任务,看看你完成了没有? 程序员最艰巨的任务跟编写代码没有多少关系。编码是逻辑思路的一种实践,这跟程序员日常工作中的其它任务比起来相对简单。如果你认为自己还是一...
  • 团队成员无法按期完成任务怎么办? 这是最近和朋友在圈子里面的一些讨论,针对的主题是:团队成员无法按期完成任务怎么办?估计项目经理都会遇到团队成员无法按期完成任务的情况。我们这里分析的是已经发生的时候的...
  • Quartz完成定时任务分布式单节点持久化】中我们已经完成任务的持久化,当我们创建一个任务任务会被quartz定时任务框架自动持久化到数据库,我们采用的是SpringBoot项目托管的dataSource来完成的数据源提供,...
  • 定时任务框架quartz、elastic-job和xxl-job分析定时任务框架quartz、elastic-job和xxl-job分析一、概述二、对比三、总结四、分析elastic-job-lite框架4.1 概述4.2 架构图4.3 作业启动流程图4.4 作业执行流程图4.5 ...
  • atititt.java定时任务框架选型Spring Quartz 总结    1. 。Spring Quartz (ati recomm) 1 2. Spring Quartz具体配置 2 2.1. 增加context,task命名空间xml: 2 2.2. 增加xsi:schemaLocation ...
  • 5分钟学会试用期工作总结

    万次阅读 多人点赞 2018-07-11 13:50:17
    试用期个人总结 尊敬的领导: 感谢您在百忙之中抽出珍贵的时间看我的试用期工作总结。 时光飞逝,转眼间已经过了三个月的时光。意味着我的试用期生涯已接近尾声,首先特别感谢 公司领导和同事们给予我...
  • 自己操作系统学习总结

    万次阅读 多人点赞 2017-05-04 23:26:21
    怎样自己一个简单的操作系统? https://www.zhihu.com/question/20207347 我的时候一些经历: 第一次的时候3000行左右的就无法调试了,当时主要参考了linux 0.11(基础太潜)。 第二次的时候,基本定下...
  • ①、报错任务计划程序报操作员或系统管理员拒绝了请求 设置为每天执行一次,我的程序只能执行一次,再到相应时间后就提示如下错误: 任务计划程序报操作员或系统管理员拒绝了请求(0x800710E0) 有大神会吗?求教 ...
  • 13 Python学习总结:多任务,多线程

    千次阅读 2019-07-31 23:44:20
    一般情况下我们可以使用多线程来完成任务的开发,一个进程中包含了多个线程,对系统的开销较小一个进程中至少有一个线程,默认的这个线程我们叫做主线程。 单线程模拟人脑: import time # 敲键盘ing ...
  • 小猪的Git使用总结

    万次阅读 多人点赞 2017-01-11 19:26:04
    小猪用Git一年多后的精华总结标签: Git描述性文字:不要问我为什么用这种骚猪风格的标题,现在博文,标题不骚,人家都不乐意看~接触Git到现在有一年多了,对Git使用也是日渐精进,虽说不上很熟络,但也算 ...
  • 我们在之前有讲过SpringBoot是已经集成了定时任务的,详见:第二十六章:SpringBoot使用@Scheduled创建定时任务,那么我们本章将会采用外置的quartz定时任务框架来完成定时任务的分布式单节点持久化,我们为什么要...
  • Qt5对Json的读写总结 浅析

    万次阅读 2014-11-03 15:46:24
    主要任务:通过Qt5完成对Json格式的文件的读写操作。 ①首先读取json文件进行解析 ②对读取出来的东西判断是QJsonObject还是QJsonArray类型③对相应的类型根据提供的key去查找或者修改相对应的值 主要技术: (1)...
  • 文章倒太多信息量,算是“水文”吧,带来的思考是文本分类问题是有一些“线性”问题的部分,也就是说不必做过多的非线性转换、特征组合即可捕获很多分类信息,因此有些任务即便简单的模型便可以搞定了。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 441,166
精华内容 176,466
关键字:

任务没完成总结怎么写