精华内容
下载资源
问答
  • Mybatis 实现原理之 一二级缓存

    千次阅读 2018-10-15 15:19:20
    本片文章讲述的是Mybatis是如何无感知的让用户使用到一二级缓存,以及一二级缓存的实现细节实现原理。 结论:Mybatis 下文通过代码DEMO的展示, 以及源码的解说介绍JDK动态代理, Mybatis对其的应用。 ...

    引言

    对Mybatis一直都没有做实质的记录。 现记录Mybatis的一些实现细节。组成一个系列。

    本片文章讲述的是Mybatis是如何无感知的让用户使用到一二级缓存,以及一二级缓存的实现细节和实现原理。

    系列文章:Mybatis 实现原理之 JDK动态代理和XML语句执行

    结论通常意义上的Mybatis一级缓存, 指的是SqlSession级别缓存,即在同一个SqlSession下,可以共享的缓存,该缓存默认被开启(可通过每行CRUD标签<flushCache>关闭),依据查询使用的Mapper和方法名、传参等组成查询Key,复用SqlSession的时候可以直接从缓存里面查询;

    Mybatis二级缓存,指的是Mapper(namespace)级别的缓存,是全局的,需要指定cacheEnabled字段(默认为true)以及每个CRUD的<useCache>字段,以及每个Mapper的<cache>标签。即每个Mapper下相同的查询条件,会直接从缓存中获取结果。

    2019年04月10日19:01:37 更新:
    关于一级缓存中的SqlSession,它是如何实现全局的SqlSession,又实现一级缓存的效果的呢~ 详见 Mybatis的一级缓存 – 基于SqlSession


    SqlSession的生命周期

    本博文只谈及Spring环境下的SqlSession

    在博文Mybatis 实现原理之 JDK动态代理和XML语句执行中有提到,Spring环境中的SqlSession实现者是SqlSessionTemplate, 但同时,它使用到了包装器模式。实际包装的, 依然是DefaultSqlSession

    包装器中的DefaultSqlSession在应用程序的运行中, 会频繁的创建和销毁。
    Spring管理的SqlSessionTemplate会贯穿整个Application。


    SqlSession的创建 – 动态代理和包装器

    Mybatis的Mapper的执行,依托于MapperProxy这个代理类。在博文Mybatis 实现原理之 JDK动态代理和XML语句执行介绍了Mapper代理的创建和执行过程。

    SqlSessionTemplate 的使用非常巧妙,它委托给Spring管理, 生命周期为整个Application。但是实际的执行者并不是它。而是它的一个类元素SqlSessionTemplate#sqlSessionProxy这个代理类,同样,代理类的生命周期也是贯穿整个Application。它会频繁的创造DefaultSqlSession用于SQL的执行。

    那么, 它是怎么实现SqlSession创建和事务内复用的呢?

    sqlSessionProxy, 顾名思义, 是一个代理类。实际的代理类是SqlSessionTemplate.SqlSessionInterceptor这个InvocationHandler。它会根据当前的事务环境重新创建或者复用SqlSession。创建时序图如下:

    执行Mapper方法MapperProxy代理执行SqlSessionSqlSessiongetSqlSession()真正的执行调用invoke方法动态代理执行SqlSession的CRUD方法SqlSessionTemplate的元素sqlSessionProxy是被代理的根据当前的事务情况,创建或者返回复用的SqlSession这里面可能还涉及到一二级缓存执行Mapper方法MapperProxy代理执行SqlSessionSqlSessiongetSqlSession()真正的执行

    此处重点分析下SqlSessionUtils#getSqlSession()方法

    SqlSessionUtils创建SqlSession的方式即是SessionFactory#openSession()(DefaultSqlSessionFactory)。然后它会将创建好的SqlSession(DefaultSqlSession)放到TransactionSynchronizationManager中。存储依赖于ThreadLocal,以及当前程序运行的事务环境(基于AOP)。因此:

    • 可以很好的做到线程隔离。
    • 一个线程下,一个SessionFactory创建的SqlSession在整个线程周期的可传递的事务中复用。
    • 自然,跨线程的一级缓存无从谈起,没有事务也没有一级缓存。但是还是有可能用上二级缓存。
    • 只支持Spring管理的事务,才会复用SqlSession(启用一级缓存)。

    在非Spring环境中,源于Mybatis的各项构造相对麻烦, 一般写法也是通过DefaultSqlSessionFactory#openSession()去创建SqlSession。且一般都是即用即创建。每次创建完毕之后便等待垃圾回收器去回收。不建议全局复用一个SqlSession


    Mybatis的一级缓存 – 基于SqlSession

    Mybatis的一级缓存默认开启,虽然Mybatis的一级缓存是SqlSession缓存,但是它储存位置并不是在SqlSession, 而是Executor中。是一个HashMap(PerpetualCache持有)。

    在博文Mybatis 实现原理之 JDK动态代理和XML语句执行有讲到, SqlSession的CRUD基于Executor。它的实现类都实现了一级缓存:优先从localCache获取,获取不到的情况下再从数据库中查询

    具体逻辑可以参考如下代码片段(org.apache.ibatis.executor.BaseExecutor#query):

          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
          if (list != null) {
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
          } else {
            // 从数据库里面查询
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
          }
    

    需要注意的是缓存的Key, 参见#createCacheKey方法,另参见CacheKey#hashCode()CacheKey#equals(Object)

    2019年04月10日更新
    如上文 SqlSession的创建 – 动态代理和包装器 描述, Spring环境中, SqlSessionTemplate 生命周期贯穿整个Application,而一级缓存又是基于SqlSession、当前线程。 这其中是不是有什么矛盾?
    *
    实际不矛盾⬇️⬇️⬇️ 此SqlSession并非Spring的SqlSessionTemplate

    SqlSessionTemplate的CRUD依托于SqlSessionTemplate.SqlSessionInterceptor这个InvocationHandler(SqlSessionTemplate本身不执行任何CRUD)。
    动态代理会依据当前的线程运行情况依据全局的DefaultSqlSessionFactory创建或者复用SqlSession。并会在任意命令之后完毕之后选择关闭创建的SqlSession


    Mybatis的一级缓存的可行性分析

    Spring环境中,对于缓存的使用,最起码的条件就是:

    分支条件 实现方案 是否满足
    同SQL Query,同结果 CacheKey/localCache 满足
    数据有Update,不再查询缓存 SqlSession,所有查询都执行clear()方法 满足
    只在事务中生效 当前线程、当前事务共享SqlSession 满足
    不同的事务隔离级别下,缓存生效与否 区分不同的传播行为 满足

    理论上的一级缓存,只应该存在于事务之中,且无关具体数据源的事务隔离级别。
    Spring环境中, Mybatis的SqlSession,在开启了事务、同一个线程、同一个SqlSessionFactory中,就会共享。这就意味着一级缓存也会共享。
    Mybatis通过相同的Mapper+Method+Parameter等参数定位具体缓存。
    每次的Update(insert/delete)语句,都会导致缓存被清空。
    无关数据库事务隔离级别配置, 在Spring默认配置下, Mybatis的一级缓存可行度非常高。
    需要注意的事情是,Mybatis的一级缓存只是用的HashMap,未做容量限制。如果查询出来的结果集特别大,最好还是默认关闭一级缓存

    Mybatis的一级缓存的作用域 – 当前线程,当前事务

    Mybatis的一级缓存是可行的, 如通过源码分析来展示它的作用域。

    Spring环境中Mybatis的SqlSession(一级缓存) 需要基于事务。在事务中才会获取到当前事务、当前线程共享的SqlSession

    基于当前线程
    TransactionSynchronizationManager#doGetResource(Object)
    这里的actualKey是SqlSessionFactoryresources是一个ThreadLocal。
    返回的value是SqlSessionHolder

    	private static Object doGetResource(Object actualKey) {
    		Map<Object, Object> map = resources.get();
    		if (map == null) {
    			return null;
    		}
    		Object value = map.get(actualKey);
    		// OTHER
    		return value;
    	}
    

    基于事务
    在获取SqlSession之后, 当前线程会尝试着将其缓存。
    通过 TransactionSynchronizationManager.isSynchronizationActive() 判断是不是要缓存这个SqlSession
    它依据一个类元素TheadLocal是否存放了值来判断 true/false。
    而这个TheadLocal的set方法, 必须在启用了事务之后才会被调用。
    只要启动了事务,且事务隔离级别满足需要。几乎都会缓存SqlSession
    见: AbstractPlatformTransactionManager#getTransaction(TransactionDefinition)
    在层级调用之后, 在TransactionSynchronizationManager#initSynchronization()里面塞了默认值。

    else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
        definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
        definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) 
    

    如上, Spring环境中Mybatis的SqlSession的缓存, 几乎只基于事务传播类别。而缓存的SqlSession, 放在ThreadLocal中。自然, 一级缓存作用域也就是 当前线程,当前事务

    Mybatis的二级缓存 – 基于Mapper(namespace)

    在Mybatis中, 使用到了大量的包装器模式、动态代理。二级缓存也如此。

    Mybatis的二级缓存 – 依赖CachingExecutor

    在博文Mybatis 实现原理之 JDK动态代理和XML语句执行中有提到,Mybatis的SqlSession的执行, 实质上依赖于Executor的执行。

    Executor
    |_BaseExecutor
       |_SimpleExecutor
    |_CachingExecutor
    

    而二级缓存, 依赖的就是类:org.apache.ibatis.executor.CachingExecutor。 其获取方式如下Configuration#newExecutor(Transaction, ExecutorType):

      public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        if (ExecutorType.BATCH == executorType) {
          executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
          executor = new ReuseExecutor(this, transaction);
        } else {
          executor = new SimpleExecutor(this, transaction);
        }
        if (cacheEnabled) {
          executor = new CachingExecutor(executor);
        }
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
      }
    

    这里的cacheEnabled字段是默认为True的。

    在开启(默认不开启)了二级缓存之后, 会优先从二级缓存里面取。 如下代码片段:

      @Override
      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
          throws SQLException {
        Cache cache = ms.getCache();
        if (cache != null) {
          flushCacheIfRequired(ms);
          if (ms.isUseCache() && resultHandler == null) {
            ensureNoOutParams(ms, parameterObject, boundSql);
            @SuppressWarnings("unchecked")
            List<E> list = (List<E>) tcm.getObject(cache, key);
            if (list == null) {
              list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
              tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
          }
        }
        return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
    

    如上优先从HashMap中取值, 实现二级缓存。Key还是一级缓存的那个Key.
    同时也不难发现, 二级缓存的优先级是高于一级缓存的。

    Mybatis的二级缓存 – 如何开启关闭

    从之前的代码片段中可以发现, Mybatis的查询默认使用的是CachingExecutor(cacheEnabled默认 true开启)。 但是它还有三个控件:

    • <select> SQL语句XML标签上的 flushCache 字段。默认 false开启。可控制一级缓存和二级缓存的开启(true为关闭)
    • <select> SQL语句XML标签上的 useCache 字段。默认 true开启
    • XML Mapper的文件上增加<cache></cache>标签。默认 无关闭

    也就是说Mybatis的二级缓存默认是被关闭的。默认添加标签就可以开启。 至于关闭, 从上述四个控件着手即可:

    • cacheEnabled全局关闭, 查询都使用SimpleExecutor
    • useCache单条关闭,只影响当前SQL
    • flushCache不建议使用,它的工作原理是在查询缓存前清空缓存。数据还是会缓存,包括一级和二级缓存。
    • 不要写 <cache></cache>标签

    对于上述的二级缓存开启/关闭方法中, 除了 <cache></cache> 之外, 还可以使用 @CacheNamespace 在 Mapper上。
    @CacheNamespace与<cache></cache> 冲突, 只能选一个。
    在使用的时候往往发现它不生效, 是因为XML标签只与XML标签搭配。注解只与注解搭配。
    如: <cache></cache> 开启即可生效。
    或者:@CacheNamespace 必须搭配 @Select("select a from b")

    Mybatis的二级缓存 – 实现源码解析

    二级缓存是被存放在MappedStatement#cache中(每条SQL语句), 他们持有的是当前Mapper(namespace)的缓存的引用, 也就是说二级缓存是一个namespace级别的缓存。

    通过之前的代码片段可以看到, 缓存的获取通过Mybatis的一个工具TransactionalCacheManager来取出。实际的缓存K/V存放数据结构非常复杂。Key与一级缓存的Key一致, 参见CacheKey, V层次较深, 也大量使用到了包装器模式, 包装层次为:

    TransactionalCache -> SynchronizedCache -> LoggingCache -> SerializedCache -> LruCache -> PerpetualCache -> HashMap
    

    在开启了二级缓存之后, 便会给相应的namespace创建二级缓存Cache, MappedStatement#cache才会有值。如下代码片段才会走 if 语句(CachingExecutor#query):

        Cache cache = ms.getCache();
        if (cache != null) {
            // 查缓存
        }
    

    这个缓存的创建也是非常繁杂。可参见代码XMLMapperBuilder#cacheElement(XNode)(使用注解创建的二级缓存代码类似)。

    缓存的创建方法可参见: CacheBuilder#build()跟二级缓存相关的参数, 如缓存大小、缓存刷新时间等相关的参数, 也可以在此类内被阅读(当然也可看出, Mybatis是多喜欢包装器模式)


    Mybatis的二级缓存作用域 – Namespace/跨事务

    二级缓存被区分不同的namespace创建之后, 还有一个存储过程。 它是跨事务的。
    同时, 因为二级缓存被MappedStatement持有, MappedStatement的生命周期为Application, 因此二级缓存的生命周期也延续整个Application

    为什么说二级缓存是Namespace级别的缓存?

    Mybatis源码中的查询大致时序:

    执行Mapper方法MapperProxy代理执行SqlSessionExecutor执行调用invoke方法动态代理,寻找MapperMethod执行SqlSession的CRUD方法MappedStatement是具体接口->>方法的映射通过查询缓存/查询数据库执行Mapper方法MapperProxy代理执行SqlSessionExecutor执行

    这里的查缓存,就是CachingExecutorMappedStatement里面的二级缓存。而这个二级缓存,是在装配XML获取到XML的Mapper映射时, 根据<cache></cache>这个标签所建立的。MappedStatement持有的,与其对应的namespace是同一个对象。

    二级缓存是跨事务的, 在当前事务中只能享受一级缓存, 无法使用到二级缓存。
    毕竟在第一次事务中,已经可以直接从以及缓存取值了。
    那么,二级缓存是怎么从一级缓存中跑到二级缓存中去的呢?

    当一个Spring管理的事务执行完毕, 对应的事务则需要commit。毕竟实际的SQL执行者还是Executor,自然是委托给Executor#commit(boolean)。在对数据库发完毕commit命令的时候,实现类CachingExecutor也会对TransactionalCacheManager执行commit。 也就是把当前事务查询到的缓存,提交到二级缓存里面去。

    我理解这么做大致有这么些好处:
    1.缓存一致性。只有被提交的事务,它的查询才有意义被提交成为二级缓存。
    2.事务中本身已经能查一级缓存,没必要在事务中将没有完毕的事务数据提交到二级缓存(二级缓存可以被其它线程/事务访问)。


    Mybatis的二级缓存清理 – update/依赖LRU等

    清理策略一:update(insert / update / delete 语句)操作。
    清理策略二:完全依赖这几个缓存类:TransactionalCache -> SynchronizedCache -> LoggingCache -> SerializedCache -> LruCache -> PerpetualCache -> HashMap他们需要是有序的,但是不一定全都有。他们的用处分别是:

    类型 描述 作用
    TransactionalCache 事务中缓存由这个管理,缓存事务管理 事务提交时将缓存提交
    SynchronizedCache 都有,同步器,在所有方法前加上synchronized 多线程抢占
    LoggingCache 记录日志 在DEBUG级别下记录缓存命中率
    SerializedCache 序列化到IO 以ObjectOutputStream存Value
    LruCache LinkedHashMap存最远最少访问(还有一个FIFO Cache) 超过配置Size(不是字节大小)后删除最老的Value
    PerpetualCache 和一级缓存一样, 包装器 包装HashMap
    HashMap 实际存储 存<CacheKey, 数据>

    对于更新时候的清理, 则是在所有的update语句执行的时候,将缓存清理掉。 这个的机理类似于XML里面的flushCache这个字段。 只不过在<select> 语句中它默认是false, 在<update>/<insert>/<delete>语句中, 它默认是true。参见:

      @Override
      public int update(MappedStatement ms, Object parameterObject) throws SQLException {
        flushCacheIfRequired(ms);
        return delegate.update(ms, parameterObject);
      }
      
      private void flushCacheIfRequired(MappedStatement ms) {
        Cache cache = ms.getCache();
        if (cache != null && ms.isFlushCacheRequired()) {      
          tcm.clear(cache);
        }
      }
    

    为什么任何的update SQL都可以清理掉整个namespace的缓存呢?

    • 一个namespace(Mapper)下的所有的SQL语句共享一个二级缓存(xml还可以写成多份)。
    • SQL语句约等于MappedStatement。它们都持有namespace的二级缓存的引用。
    • SELECT默认的flushCachefalse, UPDATE默认true。它们操作同一份。
    • 一旦有UPDATE执行,二级缓存就清空了。

    从上面的描述中也可以得出,不同的namespace(Mapper)下的SQL, 持有的二级缓存是不一致的。也就是说,对于同样的数据库表, 另一个namespace(Mapper)下的UPDATE SQL, 无法清空当前namespace(Mapper)下的二级缓存。

    也就是说,同样的表的CRUD, 如果启用了二级缓存, 得写在一块。否则会出现缓存脏读


    总结

    Mybatis的一级缓存基于SqlSession, 在一个线程、一次事务上(这样才能获得同一个SqlSession)才生效。 默认开启, 通过flushCache这个标签关闭(不建议)。 每次更新语句执行之后就会被清空。


    Mybatis的二级缓存基于namespace(Mapper)

    • 开启比较麻烦
      • 需要使用XML里面的<cache>\</cache>标签。
      • 或者使用@CacheNamespace注解。
      • 上两者不能共同使用。
      • @CacheNamespace注解只能搭配@select使用。不能搭配XML语句。
    • 生命周期很长, 整个Application
      • 一个namespace下所有SQL共享。
      • 遇见UPDATE语句之后就会被清空。
      • 只有在事务提交之后才会被提交到二级缓存中更新原有缓存。
      • 不开启事务也可以用。每次Query都会提交到二级缓存。
    • 优先级高于一级缓存。
    • 使用到二级缓存,需要保证CRUD在一个namespace(Mapper)下。
    展开全文
  • 资料来源:教育部关于研究生学科、专业目录的更新 2011年,教育部、国务院学位委员会颁发了《关于印发<...2015年6月11日,国务院学位委员会、教育部颁发《关于增设网络空间安全一级学科的通知》...
        

    资料来源:教育部关于研究生学科、专业目录的更新

    2011年,教育部、国务院学位委员会颁发了《关于印发<学位授予和人才培养学科目录(2011年)>的通知》(学位〔2011〕11号),规定自印发之日起,学位授权审核及学位与研究生教育质量监督工作按照新目录进行。2015年6月11日,国务院学位委员会、教育部颁发《关于增设网络空间安全一级学科的通知》(学位〔2015〕11号),在“工学”门类下增设“网络空间安全”一级学科,学科代码为“0839”,授予“工学”学位。上述文件均已在教育部政府门户网站上公开。

    目前,研究生培养和学位授予工作按照《学位授予和人才培养学科目录(2011年)》进行。根据《学位授予和人才培养学科目录设置与管理办法》(学位〔2009〕10号),一级学科的调整每10年进行一次,调整后向社会公开。

    一级和二级学科

    01 哲学

    0101 哲学

    02 经济学

    0201 理论经济学
    0202 应用经济学

    03 法学

    0301 法学
    0302 政治学
    0303 社会学
    0304 民族学
    0305 马克思主义理论
    0306 公安学

    04 教育学

    0401 教育学
    0402 心理学(可授教育学、理学学位)
    0403 体育学

    05 文学

    0501 中国语言文学
    0502 外国语言文学
    0503 新闻传播学

    06 历史学

    0601 考古学
    0602 中国史
    0603 世界史

    07 理学

    0701 数学
    0702 物理学
    0703 化学
    0704 天文学
    0705 地理学
    0706 大气科学
    0707 海洋科学
    0708 地球物理学
    0709 地质学
    0710 生物学
    0711 系统科学
    0712 科学技术史(分学科,可授理学、工学、农学、医学学位)
    0713 生态学
    0714 统计学(可授理学、经济学学位)

    08 工学

    0801 力学(可授工学、理学学位)
    0802 机械工程
    0803 光学工程
    0804 仪器科学与技术
    0805 材料科学与工程(可授工学、理学学位)
    0806 冶金工程
    0807 动力工程及工程热物理
    0808 电气工程
    0809 电子科学与技术(可授工学、理学学位)
    0810 信息与通信工程
    0811 控制科学与工程
    0812 计算机科学与技术(可授工学、理学学位)
    0813 建筑学
    0814 土木工程
    0815 水利工程
    0816 测绘科学与技术
    0817 化学工程与技术
    0818 地质资源与地质工程
    0819 矿业工程
    0820 石油与天然气工程
    0821 纺织科学与工程
    0822 轻工技术与工程
    0823 交通运输工程
    0824 船舶与海洋工程
    0825 航空宇航科学与技术
    0826 兵器科学与技术
    0827 核科学与技术
    0828 农业工程
    0829 林业工程
    0830 环境科学与工程(可授工学、理学、农学学位)
    0831 生物医学工程(可授工学、理学、医学学位)
    0832 食品科学与工程(可授工学、农学学位)
    0833 城乡规划学
    0834 风景园林学(可授工学、农学学位)
    0835 软件工程
    0836 生物工程
    0837 安全科学与工程
    0838 公安技术

    09 农学

    0901 作物学
    0902 园艺学
    0903 农业资源与环境
    0904 植物保护
    0905 畜牧学
    0906 兽医学
    0907 林学
    0908 水产
    0909 草学

    10 医学

    1001 基础医学(可授医学、理学学位)
    1002 临床医学
    1003 口腔医学
    1004 公共卫生与预防医学(可授医学、理学学位)
    1005 中医学
    1006 中西医结合
    1007 药学(可授医学、理学学位)
    1008 中药学(可授医学、理学学位)
    1009 特种医学
    1010 医学技术(可授医学、理学学位)
    1011 护理学(可授医学、理学学位)

    11 军事学

    1101 军事思想及军事历史
    1102 战略学
    1103 战役学
    1104 战术学
    1105 军队指挥学
    1106 军制学
    1107 军队政治工作学
    1108 军事后勤学
    1109 军事装备学
    1110 军事训练学

    12 管理学

    1201 管理科学与工程(可授管理学、工学学位)
    1202 工商管理
    1203 农林经济管理
    1204 公共管理
    1205 图书情报与档案管理

    13 艺术学

    1301 艺术学理论
    1302 音乐与舞蹈学
    1303 戏剧与影视学
    1304 美术学
    1305 设计学(可授艺术学、工学学位)

    附:
    专业学位授予和人才培养目录

    0251 金融 0853 城市规划
    0252 应用统计 0951 农业推广
    0253 税务 0952 *兽医
    0254 国际商务 0953 风景园林
    0255 保险 0954 林业
    0256 资产评估 1051 *临床医学
    0257 审计 1052 *口腔医学
    0351 法律 1053 公共卫生
    0352 社会工作 1054 护理
    0353 警务 1055 药学
    0451 *教育 1056 中药学
    0452 体育 1151 军事
    0453 汉语国际教育 1251 工商管理
    0454 应用心理 1252 公共管理
    0551 翻译 1253 会计
    0552 新闻与传播 1254 旅游管理
    0553 出版 1255 图书情报
    0651 文物与博物馆 1256 工程管理
    0851 建筑学 1351 艺术
    0852 *工程

    注:名称前加“*”的可授予硕士、博士专业学位;“建筑学”可授予学士、硕士专业学位;其它授予硕士专业学位。

    展开全文
  • 是整理前馈神经网络中正向传播、误差反向传播和梯度下降的原理;三是梯度消失梯度爆炸问题的原因及解决思路。 、神经网络结构 目前比较常用的神经网络结构有如下三种: 1、前馈神经网络 前馈神经网络...

    转自:https://www.cnblogs.com/Luv-GEM/p/10694471.html

    这篇文章主要整理三部分内容,一是常见的三种神经网络结构:前馈神经网络、反馈神经网络和图网络;二是整理前馈神经网络中正向传播、误差反向传播和梯度下降的原理;三是梯度消失和梯度爆炸问题的原因及解决思路。

    一、神经网络结构

    目前比较常用的神经网络结构有如下三种:

    1、前馈神经网络

    前馈神经网络中,把每个神经元按接收信息的先后分为不同的组,每一组可以看做是一个神经层。每一层中的神经元接收前一层神经元的输出,并输出到下一层神经元。整个网络中的信息是朝着一个方向传播的,没有反向的信息传播(和误差反向传播不是一回事),可以用一个有向无环图来表示。

    前馈神经网络包括全连接前馈神经网络和卷积神经网络。

    前馈神经网络可以看做是一个函数,通过简单非线性函数的多次复合,实现输入空间到输出空间的复杂映射。

    2、反馈神经网络

    反馈神经网络中神经元不但可以接收其他神经元的信号,而且可以接收自己的反馈信号。和前馈神经网络相比,反馈神经网络中的神经元具有记忆功能,在不同时刻具有不同的状态。反馈神经网络中的信息传播可以是单向也可以是双向传播,因此可以用一个有向循环图或者无向图来表示。

    常见的反馈神经网络包括循环神经网络、Hopfield网络和玻尔兹曼机。

    而为了进一步增强记忆网络的记忆容量,可以映入外部记忆单元和读写机制,用来保存一些网络的中间状态,称为记忆增强网络,比如神经图灵机。

    3、图网络

    前馈神经网络和反馈神经网络的输入都可表示为向量或者向量序列,但实际应用中很多数据都是图结构的数据,比如知识图谱、社交网络和分子网络等。这时就需要用到图网络来进行处理。

    图网络是定义在图结构数据上的神经网络,图中每个结点都由一个或者一组神经元组成。结点之前的连接可以是有向的,也可以是无向的。每个结点可以收到来自相邻结点或自身的信息。

    以下是这三种神经网络结构的示意图:

     

    二、前馈神经网络

    在前馈神经网络(Feedforward Neural Network, FNN )中,每一层的神经元可以接收前一层神经元的信号,并产生信号输出到下一层。第0层叫做输入层,最后一层叫做输出层,其他中间层叫做隐藏层。整个网络中无反馈,信号从输入层向输出层单向传播,可用一个有向无环图表示。

    多层前馈神经网络的图示如下:

    1、前馈神经网络的前向传播

    用下面的记号来描述一个前馈神经网络:

    前馈神经网络通过下面公式进行信息传播:

    样前馈神经网络通过逐层的信息传递,得到网络最后的输出a(L)。整个网络可以看做是一个复合函数,将向量x作为第1层的输入a(0),将第L层的输出a(L)作为整个函数的输出。

     2、前馈神经网络的梯度下降法

    神经网络具有极其强大的拟合能力,可以作为一个万能函数来使用,通过进行复杂的特征转换,可以以任意精度来近似任何一个有界闭集函数。

    类似于其他机器学习算法求解参数的数值计算方法,我们首先考虑用梯度下降法来进行参数学习。

    如果采用交叉熵损失函数,对于样本(x,y),其损失函数为:

    其中y∈{0, 1}C是标签y对应的one-hot向量表示,C是类别的个数。

    给定训练集D={(x(1), y(1)),(x(2), y(2)),...,(x(N), y(N))},将每个样本x(n)输入给前馈神经网络,得到神经网络的输出后,其在训练集D上的结构化风险函数为:

    其中W和b分别表示网络中所有的权重矩阵和偏置向量。是正则化项,公式为:

    然后用梯度下降法来进行学习。在梯度下降法的每次迭代中,第l层的参数W(l)和b(l)的参数更新方式为:

    梯度下降法需要计算损失函数对参数的偏导数,如果用链式法则对每个参数逐一求偏导,涉及到矩阵微分,效率比较低。所以在神经网络中经常使用反向传播算法来高效地计算梯度。

    3、前馈神经网络的误差反向传播算法

    假设给定一个样本(x,y),将其输入到神经网络模型中,得到损失函数为:,如果要采用梯度下降法对神经网络的参数进行学习,那么就要计算损失函数关于每个参数的导数。

    我们就拿第l层中的参数矩阵W(l)和b(l)为例,计算损失函数对参数矩阵的偏导数。但因为的计算涉及到矩阵微分,非常繁琐,于是我们先计算W(l)中某个元素的偏导数。根据链式法则有:

    上面两个公式中的第二项都是目标函数关于第l层的神经元z(l)的偏导数,称为误差项。那么我们需要计算三个偏导数:

    (1)计算偏导数

    由于z(l)和Wij(l)的函数关系为,所以偏导数为

     其中Wi:(l)为权重矩阵W(l)的第i行。

    (2)计算偏导数

    因为z(l)与b(l)的函数关系为,因此偏导数是一个维度是m(l) × m(l) 的单位矩阵。

     (3)计算误差项

    用δ(l)来定义第l层神经元的误差项,它用来表示第l层神经元对最终损失的影响,也反映了最终损失对第l层神经元的敏感程度。

    根据链式法则,第l层的误差项为:

    首先根据,其中fl(•)是按位计算的函数,计算的偏导数如下。diag(•)表示对角矩阵。

    然后根据,有:

    于是第l层的误差项δ(l)最终表示为:

    其中⊙表示向量的点积运算符,表示每个元素相乘。

    上面这个公式就是误差的反向传播公式!因为第l层的误差项可以通过第l+1层的误差项计算得到。反向传播算法的含义是:第l层的一个神经元的误差项等于该神经元激活函数的梯度,再乘上所有与该神经元相连接的第l+1层的神经元的误差项的权重和。

    再回到开头求两个参数的公式:

    里面的三个偏导数都已经求出来了。于是可以求出:

    进一步,损失函数关于第l层权重W(l)梯度为:

    而损失函数关于第l层偏置b(l)的梯度为:

    于是在利用误差反向传播算法计算出每一层的误差项后,就可以得到每一层参数的梯度。

    基于误差反向传播算法(backpropagation,BP)的前馈神经网络训练过程可以分为以下三步:

    1、在前向传播时计算每一层的净输入z(l)和激活值a(l),直至最后一层;

    2、用误差反向传播计算每一层的误差项 δ(l);

    3、计算每一层参数的偏导数,并更新参数。

    使用随机梯度下降的误差反向传播算法的具体训练过程如下:

     

    三、梯度消失和梯度爆炸

    训练神经网络,尤其是深度神经网络时,面临的一个问题是梯度消失或者梯度爆炸,也就是神经元上的梯度会变得非常小或者非常大,从而加大训练的难度。

    1、原理

    梯度消失(Vanishing Gradient Problem,或称梯度弥散)的意思是,在误差反向传播的过程中,误差经过每一层传递都会不断衰减,当网络层数很深时,神经元上的梯度也会不断衰减,导致前面的隐含层神经元的学习速度慢于后面隐含层上的神经元。

    在上面我们得到了神经网络中误差反向传播的迭代公式:

    误差从输出层反向传播时,在每一层都要乘以该层的激活函数的导数,如果导数的值域小于1,甚至如Sigmoid函数在两端的饱和区导数趋于0,那么梯度就会不断衰减甚至消失。

    与之相对的问题是梯度爆炸(Exploding Gradient Problem),也就是前面层中神经元的梯度变得非常大。与梯度消失不太一样的是,梯度爆炸通常产生于过大的权重W。

    总结一下就是:梯度消失通常出现在深层网络和采用了不合适的损失函数(sigmoid)的情形,梯度爆炸一般出现在深层网络和权值初始化值太大的情况下。

    2、举例

    可以通过一个例子来更好的理解这两个问题。来看一个简单的深度神经网络:每一层都只有一个神经元。如下图是有三层隐含层的神经网络:

    这里w表示权重,b是偏置值,C是代价函数。然后通过计算代价函数关于第一个隐含神经元的偏置的梯度,以及关于第三个隐含神经元偏置的梯度,来考察梯度消失和梯度爆炸问题。

    经过推导可得梯度的公式:

     

    (1)梯度消失

    首先看公式中的导数σ'(z)。激活函数如果采用Sigmoid函数,则其导数为:,也就是说导数的最大值为0.25。

    其次看公式中的权重w。如果用均值为0标准差为1的高斯分布来初始化网络的权重,那么所有的权重会满足|wj|<1。

    那么就会与wjσ'(zj)<0.25,于是梯度公式中所有项相乘时,梯度以指数级下降;神经网络的层数越深,梯度的值下降得更快。

    然后再与第三个隐含神经元的梯度进行对比,如下图。可见前面的神经元的偏置的梯度是后面的1/16甚至更小。所以梯度消失产生的原因主要就是激活函数的导数值太小。

     

    (2)梯度爆炸

    梯度爆炸的一个原因是网络的权重设置得过大,比如在上图中,让wj=100 。然后选择偏置使得σ'(zj)项不会太小,比如σ'(zj)=0.25,那么wjσ'(zj)=25。代入上面的梯度公式中,可以看到梯度以25的指数级增大,神经网络的层数越深,梯度越大。这就带来了梯度爆炸的问题。

     3、解决

    那么如何解决梯度消失和梯度爆炸的问题呢?

    一般用以下几种方法来解决梯度消失和梯度爆炸的问题。

    (1)对于梯度消失问题,可以选择导数比较大的激活函数,比如ReLU激活函数。

    Sigmoid函数和Tanh函数都属于两端饱和型激活函数,使用这两种激活函数的神经网络,在训练过程中梯度通常会消失,因此可以选择其他激活函数来替代。

    ReLU激活函数在正数部分的导数恒为1,每层网络都可以得到相同的更新速度,因此在深层网络中不会产生梯度消失和梯度爆炸的问题。

    2)对于梯度爆炸问题,可以采取梯度截断和权重正则化的方法。

    梯度截断这个方案主要是针对梯度爆炸提出的,其思想是设置一个梯度截断阈值,然后在更新梯度的时候,如果梯度超过这个阈值,那么就将其强制限制在这个范围之内。

    权重正则化就是在损失函数中,加入对网络的权重进行惩罚的正则化项,比如权重的L1正则化或者L2正则化。如果发生梯度爆炸,权值的范数会变得非常大,通过正则化项进行惩罚,可以限制权重的值,从而减轻梯度爆炸的问题。

    (3)选择更好的随机初始化权重的方法。

    一是对每个权重都进行随机初始化,尽量让每个权重都不同,使不同神经元之间的区分性更好。

    二是选择合适的随机初始化权重的区间,不能太小,也不能太大。一般而言,参数初始化的区间应该根据神经元的性质进行差异化的设置,如果一个神经元的输入连接很多,那么每个输入连接上的权重要小一些,以避免神经元的输出过大(当激活函数为ReLU时,但输出为正时导数都是1)或者过饱和(当激活函数为Sigmoid函数时,梯度会接近于0)

    此外,还有Batchnorm(batch normalization)的方法,留待以后探究。

     

    参考资料:

    1、邱锡鹏:《神经网络与深度学习》

    2、Michael Nielsen:《Neural Network and Deep Learning》

    展开全文
  • 利用Python分析《庆余年》人物图谱微博传播路径

    万次阅读 多人点赞 2019-12-01 11:55:04
    利用Python分析《庆余年》人物图谱微博传播路径 庆余年电视剧终于在前两天上了,这两天赶紧爬取微博数据看一下它的表现,已经分析原著中的人物关系图谱

    利用Python分析《庆余年》人物图谱和微博传播路径

    庆余年电视剧终于在前两天上了,这两天赶紧爬取微博数据看一下它的表现。
    微博传播路径

    庆余年

    《庆余年》是作家猫腻的小说。这部从2007年就开更的作品拥有固定的书迷群体,也在文学IP价值榜上有名。
    《亲余年》书籍
    期待已久的影视版的《庆余年》终于播出了,一直很担心它会走一遍《盗墓笔记》的老路。

    在《庆余年》电视剧上线后,就第一时间去看了,真香。
    在这里插入图片描述

    庆余年微博传播分析

    《庆余年》在微博上一直霸占热搜榜,去微博看一下大家都在讨论啥:
    微博评论
    一条条看显然不符合数据分析师身份。

    于是爬取了微博超话页面,然后找到相关人员,分别去爬取相关人员的微博评论,看看大家都在讨论啥。

    import re
    import time
    import copy
    import pickle
    import requests
    import argparse
    
    '''微博爬虫类'''
    class weibo():
    	def __init__(self, **kwargs):
    		self.login_headers = {
    								'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36',
    								'Accept': '*/*',
    								'Accept-Encoding': 'gzip, deflate, br',
    								'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
    								'Connection': 'keep-alive',
    								'Origin': 'https://passport.weibo.cn',
    								'Referer': 'https://passport.weibo.cn/signin/login?entry=mweibo&r=https%3A%2F%2Fweibo.cn%2F&backTitle=%CE%A2%B2%A9&vt='
    							}
    		self.login_url = 'https://passport.weibo.cn/sso/login'
    		self.home_url = 'https://weibo.com/'
    		self.headers = {
    						'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36',
    						}
    		self.session = requests.Session()
    		self.time_interval = 1.5
    	'''获取评论数据'''
    	def getComments(self, url, url_type='pc', max_page='all', savename=None, is_print=True, **kwargs):
    		# 判断max_page参数是否正确
    		if not isinstance(max_page, int):
    			if max_page != 'all':
    				raise ValueError('[max_page] error, weibo.getComments -> [max_page] should be <number(int) larger than 0> or <all>')
    		else:
    			if max_page < 1:
    				raise ValueError('[max_page] error, weibo.getComments -> [max_page] should be <number(int) larger than 0> or <all>')
    		# 判断链接类型
    		if url_type == 'phone':
    			mid = url.split('/')[-1]
    		elif url_type == 'pc':
    			mid = self.__getMid(url)
    		else:
    			raise ValueError('[url_type] error, weibo.getComments -> [url_type] should be <pc> or <phone>')
    		# 数据爬取
    		headers = copy.deepcopy(self.headers)
    		headers['Accept'] = 'application/json, text/plain, */*'
    		headers['MWeibo-Pwa'] = '1'
    		headers['Referer'] = 'https://m.weibo.cn/detail/%s' % mid
    		headers['X-Requested-With'] = 'XMLHttpRequest'
    		url = 'https://m.weibo.cn/comments/hotflow?id={}&mid={}&max_id_type=0'.format(mid, mid)
    		num_page = 0
    		comments_data = {}
    		while True:
    			num_page += 1
    			print('[INFO]: Start to get the comment data of page%d...' % num_page)
    			if num_page > 1:
    				url = 'https://m.weibo.cn/comments/hotflow?id={}&mid={}&max_id={}&max_id_type={}'.format(mid, mid, max_id, max_id_type)
    			res = self.session.get(url, headers=headers)
    			comments_data[num_page] = res.json()
    			if is_print:
    				print(res.json())
    			try:
    				max_id = res.json()['data']['max_id']
    				max_id_type = res.json()['data']['max_id_type']
    			except:
    				break
    			if isinstance(max_page, int):
    				if num_page < max_page:
    					time.sleep(self.time_interval)
    				else:
    					break
    			else:
    				if int(float(max_id)) != 0:
    					time.sleep(self.time_interval)
    				else:
    					break
    		if savename is None:
    			savename = 'comments_%s.pkl' % str(int(time.time()))
    		with open(savename, 'wb') as f:
    			pickle.dump(comments_data, f)
    		return True
    	'''模拟登陆'''
    	def login(self, username, password):
    		data = {
    				'username': username,
    				'password': password,
    				'savestate': '1',
    				'r': 'https://weibo.cn/',
    				'ec': '0',
    				'pagerefer': 'https://weibo.cn/pub/',
    				'entry': 'mweibo',
    				'wentry': '',
    				'loginfrom': '',
    				'client_id': '',
    				'code': '',
    				'qq': '',
    				'mainpageflag': '1',
    				'hff': '',
    				'hfp': ''
    				}
    		res = self.session.post(self.login_url, headers=self.login_headers, data=data)
    		if res.json()['retcode'] == 20000000:
    			self.session.headers.update(self.login_headers)
    			print('[INFO]: Account -> %s, login successfully...' % username)
    			return True
    		else:
    			raise RuntimeError('[INFO]: Account -> %s, fail to login, username or password error...' % username)
    	'''获取PC端某条微博的mid'''
    	def __getMid(self, pc_url):
    		headers = copy.deepcopy(self.headers)
    		headers['Cookie'] = 'SUB=_2AkMrLtDRf8NxqwJRmfgQzWzkZI11ygzEieKdciEKJRMxHRl-yj83qhAHtRB6AK7-PqkF1Dj9vq59_dD6uw4ZKE_AJB3c;'
    		res = requests.get(pc_url, headers=headers)
    		mid = re.findall(r'mblog&act=(\d+)\\', res.text)[0]
    		return mid
    
    
    if __name__ == '__main__':
    	import argparse
    	parser = argparse.ArgumentParser(description="weibo comments spider")
    	parser.add_argument('-u', dest='username', help='weibo username', default='')
    	parser.add_argument('-p', dest='password', help='weibo password', default='')
    	parser.add_argument('-m', dest='max_page', help='max number of comment pages to crawl(number<int> larger than 0 or all)', default=100)
    	parser.add_argument('-l', dest='link', help='weibo comment link', default='')
    	parser.add_argument('-t', dest='url_type', help='weibo comment link type(pc or phone)', default='pc')
    	args = parser.parse_args()
    	wb = weibo()
    	username = args.username
    	password = args.password
    	try:
    		max_page = int(float(args.max_page))
    	except:
    		pass
    	**加粗样式**url = args.link
    	url_type = args.url_type
    	if not username or not password or not max_page or not url or not url_type:
    		raise ValueError('argument error')
    	wb.login(username, password)
    	wb.getComments(url, url_type, max_page)
    

    爬取到微博评论后,老规矩,词云展示一下,不同主角的评论内容差别还是挺大的

    微博评论词云分析

    不同主演的评论风格差异较大,也与微博内容息息相关。
    张若昀:
    在这里插入图片描述
    李沁:
    在这里插入图片描述
    肖战:
    emmm…算了吧
    在这里插入图片描述

    从目前大家的评论来看,情绪比较正向,评价较高,相信《庆余年》会越来越火的。

    这部剧在微博热度这么高,都是谁在传播呢?

    于是我进一步点击用户头像获取转发用户的公开信息。

    看了一下几位主演的相关微博,都是几十万的评论和转发,尤其是肖战有百万级的转发,尝试爬了一下肖战的微博,执行了6个小时只爬了十分之一。

    最终还是败给了各位小飞侠,之后有结果再同步给大家。
    在这里插入图片描述

    于是我只能挑软柿子捏,换成官微的微博。
    在这里插入图片描述
    这条微博发布时间是26号,经过一段时间已经有比较好的传播,其中有几个关键节点进一步引爆话题。
    在这里插入图片描述
    经过几个关键节点后,进一步获得传播,这几个关键节点分别是:

    肖战的超话:https://weibo.com/1081273845/Ii1ztr1BH
    王小亚的微博:https://weibo.com/6475144268/Ii1rDEN6q

    继续看一下转发该微博的用户分析:

    进一步了解转发微博的受众,掌握传播范围和深度。
    传播用户分析
    整体看下来,庆余年官微的这条微博90%都是普通用户的转发,这部剧转发层级达到5层,传播范围广,在微博上的讨论女性居多(占比89%),大部分集中在一二线城市。

    原著人物关系图谱

    如果只看微博,不分析原著,那就不是一个合格的书粉。

    于是我去下载了原著画一下人物关系图谱。
    《庆余年》人物
    先给大家看一下原著的人物关系图谱:
    在这里插入图片描述
    emmm…确实挺丑的,大家可以去Gephi上调整。

    首先我需要从原著里洗出人物名,尝试用jieba分词库来清洗:

    import jieba
    
    test= 'temp.txt' #设置要分析的文本路径
    text = open(test, 'r', 'utf-8')
    seg_list = jieba.cut(text, cut_all=True, HMM=False)
    print("Full Mode: " + "/ ".join(seg_list))  # 全模式
    

    生成一个适合你的列表

    发现并不能很好的切分出所有人名,最简单的方法是直接准备好人物名称和他们的别名,这样就能准确定位到人物关系。

    存储好人物表,以及他们对应的别名(建立成字典)

    def synonymous_names(synonymous_dict_path):
        with codecs.open(synonymous_dict_path, 'r', 'utf-8') as f:
            lines = f.read().split('\n')
        for l in lines:
            synonymous_dict[l.split(' ')[0]] = l.split(' ')[1]
        return synonymous_dict
    

    接下来,清理文本数据:

    def clean_text(text):
        new_text = []
        text_comment = []
        with open(text, encoding='gb18030') as f:
            para = f.read().split('\r\n')
            para = para[0].split('\u3000')
        for i in range(len(para)):
            if para[i] != '':
                new_text.append(para[i])
        for i in range(len(new_text)):
            new_text[i] = new_text[i].replace('\n', '')
            new_text[i] = new_text[i].replace(' ', '')
            text_comment.append(new_text[i])
        return text_comment
    

    我们需要进一步统计人物出现次数,以及不同人物间的共现次数:

    text_node = []
    for name, times in person_counter.items():
        text_node.append([])
        text_node[-1].append(name)
        text_node[-1].append(name)
        text_node[-1].append(str(times))
    node_data = DataFrame(text_node, columns=['Id', 'Label', 'Weight'])
    node_data.to_csv('node.csv', encoding='gbk')
    

    结果样例如下:
    部分人物出场次数统计
    不愧是主角,范闲出现的次数超过了其他人物出现次数的总和,基本每个人都与主角直接或间接地产生影响。
    人物出现次数分布
    同理可以得到不同人物的边,具体代码参考源文件。

    接下来需要做的就是利用Gephi绘制人物关系图谱:
    在这里插入图片描述
    运行结果:
    在这里插入图片描述
    参考文献:Ren, Donghao, Xin Zhang, Zhenhuang Wang, Jing Li, and Xiaoru Yuan. “WeiboEvents: A Crowd Sourcing Weibo Visual Analytic System.” In Pacific Visualization Symposium (PacificVis) Notes, 2014 IEEE, pp. 330-334. IEEE, 2014.

    有任何问题,欢迎私信。

    展开全文
  • 这个select 标签在执行时是不清除一级和二级缓存的,只有insert,update,delete标签 运行时会自动清除一级和二级缓存。 关键入口类 :org.apache.ibatis.session.defaults.DefaultSqlSession org....
  • 误差 ... 目前的神经网络层数可达百层以上,激活函数又是非线性的,很难用个数学表达式来表达整个神经网络的输出,即使能够找个数学表达式也是非常复杂,如果能够找到个数学表达式我们...
  • <br />二级域名绑定一级目录其实很简单。在 DNS 域名管理后台域名转发里,设置二级域名转发至你的目标一级目录。提交后,在主机记录列表会添加一个你刚才提交的二级域名,其指向的IP地址与主域名不同。比如,...
  • spring事务传播属性隔离级别

    千次阅读 2018-01-31 17:16:13
    1 事务的传播属性(Propagation) 1) REQUIRED ,这个是默认的属性 Support a current transaction, create a new one if none exists. 如果存在个事务,则支持当前事务。如果没有事务则开启
  • 由于工作需要,需要在公司顶级域名下建立二级域名,公司服务器为阿里云下云服务器。但是自己之前根本没有此类基础,因此对域名相关知识,进行了相关了解,大多数为百度百科内容。,域名相关了解这里摘录的为百度...
  • hibernate事务,一级缓存,二级缓存

    千次阅读 2012-03-06 22:03:20
    Atomic(原子性):事务中包含的操作被看作个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败。  2. Consistency(一致性):只有合法的数据可以被写入数据库,否则事务应该将其回滚到最初状态。 ...
  • 这篇文章作者将继续分析WannaCry勒索病毒,主要通过IDAOD逆向分析蠕虫传播部分,详细讲解蠕虫是如何感染传播的。同时,由于作者技术真的菜,只能叙述自己摸索的过程,如果存在错误或不足之处,还望告知。希望这篇...
  • CFA原版书是唯一100%覆盖CFA考试所有知识点的资料, 更契合考试出题者的思路,从近年来的考题中,可以看出,考试内容越来越 接近原版书原文原句,甚至是例句 链接:... ...
  • 计算机等级考试--二级Java的知识点大全

    万次阅读 多人点赞 2019-03-16 20:42:59
    章 数据结构与算法 【考点1】算法的基本概念 1、算法:是指组有穷的指令集,是解题方案的准确而完整的描述。算法不等于程序,也不等于计算方法。 2、算法的基本特征: 1)确定性,算法中每步骤都必须有...
  • 本章要求: 理解电波传播的基本特性; 了解3种电波传播的机制; 掌握自由空间阴影衰落的概念;...理解传播损耗和传播预测模型的基本概念,理解几种典型模型; 了解模型校正的概念基本方法。 2.2-自由空间的电...
  • 二级C语言考试知识点(很全)

    万次阅读 多人点赞 2018-11-30 01:38:14
    二级公共基础知识作为必考内容出现,出题形式为选择题前10道,占考试总分的10%。 考试其它比重: 1、C语言程序的结构 占总分1% 2、数据类型及其运算 占总分4% 3、选择结构程序设计 占比分的3% 4、循环结构 占比分的5...
  • 如何理解反向传播算法

    千次阅读 2018-08-25 10:07:23
    对于个算法或者模型的理解可以分为直观理解,算法理解数学证明三个层次。直观的理解能够启发思维,算法层面的理解能够消除歧义,数学证明能够提供更一般化的问题描述,为潜在的问题提供分析工具。本文试图从这三...
  • 全国首款二级分销小程序上线

    千次阅读 2017-11-21 10:18:22
    微信小程序分销是款“小程序商城+三分销”多层级一体化小程序分销成交平台。 基于小程序的传播,让分销商帮您发展分销商,轻松带领成千上万的微信用户一起为您销售产品。 一键自动分销,操作简单,裂变式分销(小...
  • 在本文中,我将使用置信度传播算法(BP)一些示例数据。开始本文之前我默认您是了解贝叶斯网络的(BN),这篇文章解释了如何计算BN中不同变量的置信度,这有助于推理。 置信传播 我在GitHub上创建了个包含...
  • 前文分享了WannaCry勒索病毒逆向分析,主要通过IDAOD逆向分析蠕虫传播部分。这篇文章将继续详细讲解WannaCry蠕虫的传播机制,带领大家详细阅读源代码,分享WannaCry勒索病毒是如何传播感染的。作者分析该病毒个...
  • 近日,渔村安全团队追踪到个利用永恒之蓝漏洞传播的挖矿程序,其具备高度的模块化较强的传播能力,在短短数日就感染了数万台用户电脑。针对该突发情况,渔村安全团队迅速组织应急工作,最终使得目前的感染情况受...
  • BP反向传播算法

    千次阅读 2014-01-20 12:12:31
    反向传播BP模型 学习是神经网络种最重要也最令人注目的特点。在神经网络的发展进程中,学习算法的研究有着十分重要的地位。目前,人们所提出的神经网络模型都是学习算 法相应的。所以,有时人们并不去...
  • MYSQL事务的传播行为看这篇就够了

    千次阅读 2019-01-10 19:10:28
    否则新建个事务运行子方法 即只要大方法里面有事务,小方法即使没有事务,也会用大事务的 说明都在沿用当前存在的事务, 2. propagation_required_new 无论当前事务是否存在,都会创建新事物运行方法 这样新事务...
  • 反向传播算法

    千次阅读 2019-09-17 14:54:49
    反向传播(英语:Backpropagation,缩写为BP)是“误差反向传播”的简称,是种与最优化方法(如梯度下降法)结合使用的,用来训练人工神经网络的常见方法。该方法计算对网络中所有权重计算损失函数的梯度。这个...
  • 是谁在耳边,说“信号正向传播,误差反向传播” ? 梦回遥远大三的计算智能课,三年前我可想不到居然有朝一日会重新"预习"它...... 每堂课、本书,当时只道是寻常,如今不经意想起,忽怀念这些瞬间。所谓成长...
  • 计算机的一级基础知识

    千次阅读 多人点赞 2018-09-19 14:36:34
    1.计算机的内存储器分为ROM(只读存储器)RAM(随机存取存储器),RAM用于存储当前使用的程序、数据、中间结果以及外表交换的数据 2.计算机的应用主要分为数值计算非数值计算两大类。科学计算也成为数值计算,...
  • MS-office计算机二级选择题大全

    万次阅读 多人点赞 2018-07-22 10:31:32
    MsOffice选择题 序号 内容 分值 ... 第部分 ... 二级公共基础知识 10 4 560道左右 第二部分 计算机基础知...
  • 先看下Spring的事务传播行为类型事务传播行为类型说明PROPAGATION_REQUIRED如果当前没有事务,就新建个事务,如果已经存在个事务中,加入到这个事务中。这是 最常见的选择。PROPAGATION_SUPPORTS支持当前事务,...
  • 、spring事务的传播机制(HibernateTransactionManager)  1)、 如果当前没有事务,就新建个事务;如果已存在个事务,就加入到这个事务中。  2)、SUPPORTS" /> 支持当前事务,如果当前没有事务,以非事务...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 106,573
精华内容 42,629
关键字:

一级传播和二级传播的区别