掘金 后端 ( ) • 2024-04-06 20:05

主要内容:

  1. 机器学习之什么是增量学习
  2. Druid连接池获取连接的底层原理
  3. 架构师的好与不好
  4. 陈寅恪,公子中的公子,教授中的教授

机器学习之什么是增量学习?

公司机器学习任务这块,这周主要解决了如何做到增量学习。

所谓增量学习,就是在已有A1模型的基础上,基于新的数据训练出一个新的A2模型,并且这个A2模型既要学会了新数据的特征,也要保留A1模型已经学会的特征。

思路一,直接用A1模型在新数据上进行训练,这种方式并不是说不可以,假如训练A1时的数据和新数据是相似的,那么是可以的,拿文本分类来举例子,假如A1模型训练的时候是3种类型,那么新数据中如果还是这3中类型,那么是可以直接基于A1模型继续训练的,但如果新数据中有新的类型,那么A1模型就不支持了。

思路二,为了应对新数据中增加了新的分类,抛弃A1模型,直接基于历史数据和新数据重新训练A2,这样训练出来的A2模型肯定是所有类型都支持的。

思路三,仍然基于A1模型,仍然只基于新数据,在训练A2模型时,把新数据按批次分别送给A1模型和当前正在训练的A2模型,这样就能得出两个模型基于相同数据所产生的预测结果,计算两个预测结果之间的差值,以及A2模型本身在新数据上的误差,将这两个误差相加得到综合误差,再用这个综合误差去调整A2模型的参数,最终使得A2模型的预测结果和A1模型的预测结果保持一致,同时又能使得A2模型能学到新数据的特征。举个例子,A1模型就相当于师父,A2模型就相当于徒弟,徒弟在学习的过程中,既学会了师父的所有招式,同时也自创了一些招式,我这种思路专业一点叫做知识蒸馏。

最终我采用的思路三,缺点时在训练A2模型,相当于要做两次预测,所以会影响训练的速度,同时也需要占用更多的显存,也会影响到训练速度。

Druid连接池获取连接的底层原理

当上层框架(Hibernate、Mybatis)在调用DataSource的getConnection()方法获取数据库连接时,如果DataSource的具体实现是DruidDataSource,那么工作原理如下。

首先DruidDataSource的核心是一个DruidConnectionHolder[]数组,它就是所谓的连接池,DruidConnectionHolder中定义了一个Connection类型的connection属性,所以DruidConnectionHolder就表示一个连接,比如如果用的Mysql,那么connection属性对应的就是ConnectionImpl对象。

当调用DruidDataSource的getConnection()方法时,核心就是从DruidConnectionHolder[]数组获取出缓存好的DruidConnectionHolder对象,获取方式的源码为:

if (maxWait > 0) {
    holder = pollLast(nanos);
} else {
    holder = takeLast();
}

也就是每次都是获取DruidConnectionHolder[]数组中最后一个DruidConnectionHolder对象,拿到DruidConnectionHolder对象后会检查数据库连接是否可用,比如,数据库重启了,应用没有重启,那么DruidConnectionHolder[]数组中对应的数据库连接其实都已经是不可用的了。

在从DruidConnectionHolder[]数组中拿到一个DruidConnectionHolder对象后,会验证该对象的数据库连接是否可用,判断方式为基于当前DruidConnectionHolder对象中的数据库连接执行一个SQL,比如:

  1. Mysql默认为:"SELECT 1"
  2. Pg默写人为:"SELECT 'x'"

当然,你可以修改Druid的配置validationQuery来修改所执行的SQL。

但是拿到DruidConnectionHolder对象后,并不是一定会进行验证,需要看Druid的其他配置,比如:

  1. testOnBorrow如果为true,那么每拿出一个DruidConnectionHolder对象,都会进行验证,所以testOnBorrow等于true比较消耗性能,因为每次从连接池获取连接都会进行验证,如果验证通过则直接返回给上层框架,如果验证不通过,则会从DruidConnectionHolder[]数组中取下一个DruidConnectionHolder对象,然后继续进行验证,如果DruidConnectionHolder[]数组中没有下一个了,则等待maxWait指定的时间,如果这段时间内有归还的DruidConnectionHolder对象,那么就直接取出来,如果超过maxWait时间还没有拿到,则会抛出GetConnectionTimeoutException。
  2. 如果testOnBorrow为false,默认就是false,那么则会判断testWhileIdle是否为true,默认为true,则会用当前时间减去DruidConnectionHolder对象的lastActiveTimeMillis,从而得到DruidConnectionHolder对象的空闲时间,也就是这个DruidConnectionHolder对象有多久时间没有执行过SQL了,如果这段空闲时间超过了timeBetweenEvictionRunsMillis,默认为60s,才会进行验证,也就是说,testWhileIdle为true表示Druid只会对那些空闲时间超过60s的连接进行验证,如果正好这60s内数据库重启了,或者对应的真实数据库连接被人工kill掉了,那么Druid是不会进行校验的,这样上层Hibernate或Mybatis拿到的就是一个不可用的数据库连接,最终基于该连接是执行不了SQL的。
  3. 当然,上层框架在使用完一个数据库连接后,会调用close()方法来关闭数据库连接,而底层其实就会调到DruidConnectionHolder对象的close()方法,从而会判断testOnReturn是否为true,默认为false,如果为true,则同样会进行验证,如果验证通过,则会将该DruidConnectionHolder对象重新putLast()到DruidConnectionHolder[]数组中,如果验证不通过那么就不会putLast()了。

另外,Druid拿到一个DruidConnectionHolder对象后,会判断配置defaultAutoCommit是否为false,如果是false,则会设置底层数据库连接的autocommit也为false。

想学习更多高级技术干货内容,可以关注我的公众号:IT周瑜

架构师的好与不好

作为公司的一名架构师,我所做的工作不仅仅是设计架构、写PPT,还包括调研新技术,公司内部培训,升级组件和架构,还有就是解决难题,在公司领导和其他同事眼中,架构师的技术是很强的,是公司技术的天花板,架构师是能够或者必须解决掉其他人解决不了的问题的。

这对我自然是好事,但不一定对公司或其他同事是好事,在程序员提升自己能力的众多方式中,解决实际问题是最有效的方式,所以作为一名程序员在遇到难题时,不要过于依赖架构师或其他人,即便自己实在解决不了,后续也应该让架构师告诉你他是怎么排查问题以及解决问题的,一方面学习知识,一方面也可以作为面试亮点。

陈寅恪,公子中的公子,教授中的教授

爷爷是陈宝箴,湖南巡抚,维新变法的重要实权支持者,父亲是陈三立,与谭嗣同、徐仁铸、陶菊存并称“维新四公子”,有“中国最后一位传统诗人”之誉。

陈寅恪,与叶企孙、潘光旦、梅贻琦并称为“清华四大哲人”,与梁启超、王国维、赵元任并称为“清华国学四大导师”,又与吕思勉、陈垣、钱穆并称为“前辈史学四大家”,被誉为“公子的公子,教授之教授”。

有些人搞学问是为了学历,而有些人是真正热爱学问,陈寅恪便是后者,没有片纸学位的陈寅恪能成为水木清华的导师,受到梁启超、鲁迅、王国维等人的认可,便是靠他真正的学问。

他的侄子问他:“您在国外留学十几年,为什么没有得个博士学位?”陈答:“考博士并不难,但两三年内被一个专题束缚住,就没有时间学其他知识了。只要能学到知识,有无学位并不重要。”

向这位三百年都难得一见的旷世奇才学习!