02-MyBatis執行Sql的流程分析

目錄

本博客着重介紹MyBatis執行Sql的流程,關於在執行過程中緩存、動態SQl生成等細節不在本博客中體現,相應內容後面再單獨寫博客分析吧。

還是以之前的查詢作為列子:

public class UserDaoTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void setUp() throws Exception{
        ClassPathResource resource = new ClassPathResource("mybatis-config.xml");
        InputStream inputStream = resource.getInputStream();
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void selectUserTest(){
        String id = "{0003CCCA-AEA9-4A1E-A3CC-06D884BA3906}";
        SqlSession sqlSession = sqlSessionFactory.openSession();
        CbondissuerMapper cbondissuerMapper = sqlSession.getMapper(CbondissuerMapper.class);
        Cbondissuer cbondissuer = cbondissuerMapper.selectByPrimaryKey(id);
        System.out.println(cbondissuer);
        sqlSession.close();
    }

}

之前提到拿到sqlSession之後就能進行各種CRUD操作了,所以我們就從sqlSession.getMapper這個方法開始分析,看下整個Sql的執行流程是怎麼樣的。

獲取Mapper

進入sqlSession.getMapper方法,會發現調的是Configration對象的getMapper方法:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    //mapperRegistry實質上是一個Map,裏面註冊了啟動過程中解析的各種Mapper.xml
    //mapperRegistry的key是接口的全限定名,比如com.csx.demo.spring.boot.dao.SysUserMapper
    //mapperRegistry的Value是MapperProxyFactory,用於生成對應的MapperProxy(動態代理類)
    return mapperRegistry.getMapper(type, sqlSession);
}

進入getMapper方法:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    //如果配置文件中沒有配置相關Mapper,直接拋異常
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      //關鍵方法
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

進入MapperProxyFactory的newInstance方法:

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  //生成Mapper接口的動態代理類MapperProxy
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }
  
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

下面是動態代理類MapperProxy,調用Mapper接口的所有方法都會先調用到這個代理類的invoke方法(注意由於Mybatis中的Mapper接口沒有實現類,所以MapperProxy這個代理對象中沒有委託類,也就是說MapperProxy幹了代理類和委託類的事情)。好了下面重點看下invoke方法。

//MapperProxy代理類
public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    //獲取MapperMethod,並調用MapperMethod
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

  @UsesJava7
  private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
      throws Throwable {
    final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
        .getDeclaredConstructor(Class.class, int.class);
    if (!constructor.isAccessible()) {
      constructor.setAccessible(true);
    }
    final Class<?> declaringClass = method.getDeclaringClass();
    return constructor
        .newInstance(declaringClass,
            MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
                | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
        .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
  }

  /**
   * Backport of java.lang.reflect.Method#isDefault()
   */
  private boolean isDefaultMethod(Method method) {
    return ((method.getModifiers()
        & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
        && method.getDeclaringClass().isInterface();
  }
}

所以這邊需要進入MapperMethod的execute方法:

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //判斷是CRUD那種方法
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

然後,通過一層一層的調用,最終會來到doQuery方法, 這兒咱們就隨便找個Excutor看看doQuery方法的實現吧,我這兒選擇了SimpleExecutor:

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      //內部封裝了ParameterHandler和ResultSetHandler
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      //StatementHandler封裝了Statement, 讓 StatementHandler 去處理
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

接下來,咱們看看StatementHandler 的一個實現類 PreparedStatementHandler(這也是我們最常用的,封裝的是PreparedStatement), 看看它使怎麼去處理的:

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
     //到此,原形畢露, PreparedStatement, 這個大家都已經滾瓜爛熟了吧
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    //結果交給了ResultSetHandler 去處理,處理完之後返回給客戶端
    return resultSetHandler.<E> handleResultSets(ps);
  }

到此,整個調用流程結束。

簡單總結

這邊結合獲取SqlSession的流程,做下簡單的總結:

  • SqlSessionFactoryBuilder解析配置文件,包括屬性配置、別名配置、攔截器配置、環境(數據源和事務管理器)、Mapper配置等;解析完這些配置後會生成一個Configration對象,這個對象中包含了MyBatis需要的所有配置,然後會用這個Configration對象創建一個SqlSessionFactory對象,這個對象中包含了Configration對象;
  • 拿到SqlSessionFactory對象后,會調用SqlSessionFactory的openSesison方法,這個方法會創建一個Sql執行器(Executor組件中包含了Transaction對象),這個Sql執行器會代理你配置的攔截器方法
  • 獲得上面的Sql執行器后,會創建一個SqlSession(默認使用DefaultSqlSession),這個SqlSession中也包含了Configration對象和上面創建的Executor對象,所以通過SqlSession也能拿到全局配置;
  • 獲得SqlSession對象后就能執行各種CRUD方法了。

以上是獲得SqlSession的流程,下面總結下本博客中介紹的Sql的執行流程:

  • 調用SqlSession的getMapper方法,獲得Mapper接口的動態代理對象MapperProxy,調用Mapper接口的所有方法都會調用到MapperProxy的invoke方法(動態代理機制);
  • MapperProxy的invoke方法中唯一做的就是創建一個MapperMethod對象,然後調用這個對象的execute方法,sqlSession會作為execute方法的入參;
  • 往下,層層調下來會進入Executor組件(如果配置插件會對Executor進行動態代理)的query方法,這個方法中會創建一個StatementHandler對象,這個對象中同時會封裝ParameterHandler和ResultSetHandler對象。調用StatementHandler預編譯參數以及設置參數值,使用ParameterHandler來給sql設置參數。

Executor組件有兩個直接實現類,分別是BaseExecutor和CachingExecutor。CachingExecutor靜態代理了BaseExecutor。Executor組件封裝了Transction組件,Transction組件中又分裝了Datasource組件。

  • 調用StatementHandler的增刪改查方法獲得結果,ResultSetHandler對結果進行封裝轉換,請求結束。

Executor、StatementHandler 、ParameterHandler、ResultSetHandler,Mybatis的插件會對上面的四個組件進行動態代理。

重要類

  • MapperProxyFactory

  • MapperProxy

  • MapperMethod

  • SqlSession:作為MyBatis工作的主要頂層API,表示和數據庫交互的會話,完成必要數據庫增刪改查功能;

  • Executor:MyBatis執行器,是MyBatis 調度的核心,負責SQL語句的生成和查詢緩存的維護;

    StatementHandler 封裝了JDBC Statement操作,負責對JDBC statement 的操作,如設置參數、將Statement結果集轉換成List集合。
    ParameterHandler 負責對用戶傳遞的參數轉換成JDBC Statement 所需要的參數,
    ResultSetHandler 負責將JDBC返回的ResultSet結果集對象轉換成List類型的集合;
    TypeHandler 負責java數據類型和jdbc數據類型之間的映射和轉換
    MappedStatement MappedStatement維護了一條<select|update|delete|insert>節點的封裝,
    SqlSource 負責根據用戶傳遞的parameterObject,動態地生成SQL語句,將信息封裝到BoundSql對象中,並返回
    BoundSql 表示動態生成的SQL語句以及相應的參數信息

    Configuration MyBatis所有的配置信息都維持在Configuration對象之中。

參考

  • https://www.cnblogs.com/dongying/p/4031382.html
  • https://blog.csdn.net/qq_38409944/article/details/82494187

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

紐西蘭南島大淹水 宣布進入緊急狀態

摘錄自2020年02月05日公視報導

紐西蘭南島最南端的南地大區,過去60個小時,當地已經降下超過1000毫米的大雨,造成多處河川暴漲或潰堤、交通要道爆發土石流,一度造成200名旅客受困,兩人受傷。迫使南地大區宣布進入緊急狀態,紐西蘭氣象局也首次發布天氣紅色警戒。

為了大家生命安全著想,當局5日上午通知6000名住在地勢低窪地區的民眾緊急撤離家園。同時採取預防性停電等措施,避免災情擴大。目前預估未來幾天仍會持續降雨,所幸雨勢將會減小。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

日本防非洲豬瘟 罰則提高為300萬日圓

摘錄自2020年2月5日民視報導

日本農林水產省表示,要避免非洲豬瘟入境,危害日本養豬和肉品產業,便考慮大幅提高非法攜帶肉品入境的罰則,個人違規將從原本的100萬日圓,提高到300萬日圓,相當於台幣84萬。

而公司法人還一口氣提高50倍,將罰5000萬日圓,約台幣1400萬,相關法案預計在這次會期,提交國會審議。日本海關光是去年10~12月,沒收的違規肉品當中,就有86件驗出非洲豬瘟,當中甚至有部分病毒,仍具有傳染力。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

總結一下 IEnumerable 的例子

本篇將圍繞 《》和《》給出的例子,總結一下對於 IEnumerable 接口的一些使用方法,希望讀者能夠從中獲得一些啟發。

框架類型的迭代

對於一個實現了 IEnumerable 接口的類型來說,開發中最常用的,就是把這個類型的對象放入到 foreach 等循環關鍵詞中進行迭代,遍歷其中的元素進行處理。

這種遍歷通常分為兩種目的:遍歷和查找。

IEnumerable 及其泛型版本 IEnumerable<T> 定義了一個類型的 “可迭代性”。這點很容易理解,系統中的很多集合類型都實現了該接口。

因此這些集合類型均可以採用 foreach 進行迭代遍歷。但是每個集合類型的迭代方式和結果是不完全相同的,這取決於集合本身的特性。例如:

  • List<>Stack<> 和 Queue<> 的迭代的順序不相同,因為數據結構本身要求是不同的
  • ConcurrentDictionary<,> 和 Dictionary<,> 在迭代時的線程安全性是不同的,因為針對線程安全的設計是不同的
  • BlockingCollection.GetConsumingEnumerable 方法返回一個會產生阻塞的消費者對象,

所以,即使都是丟進 foreach,但是效果也是不完全一樣的。使用這些,需要讀者對這些類型本身需要增進了解。

建議讀者在使用框架中實現了 IEnumerable 的類型時,一定要注意迭代的細節,可以通過 MSDN 上的文檔了解其特殊性。

Linq

Linq 是一個說小不小的話題,這裏只是說其中的 Linq To Object 部分內容。

通過 Linq 中提供的一些擴展方法,可以方便的控制對於一個 IEnumerable 對象的迭代方式。通過這些方法的應用,可以在很多時候避免複雜的條件和循環嵌套。

同時,Linq 中抽象的 Func 和 Action,也要求開發人員在平時的編寫過程中注意對於迭代本身的歸類和整理。Where(IsLeapYear) 會比 Where(x=>(x % 4 == 0 && x % 100 != 0) || x % 400 == 0) 來的更加容易閱讀。

設計複雜的數據結構及其迭代算法

除了基礎的數據結構,開發過程中有時需要自定義一些集合類型。這些集合類型需要自己實現一個迭代過程。例如:二叉樹及其遍歷,對列表進行分頁等等。

這些數據結構的迭代通常需要特定算法的支持。

在《》中關於樹的幾個例子便數據此類中。

本地函數

在 C#7.0 引入了之後, IEnumerable 結合本地函數,快速實現自定義迭代過程的奇怪操作也就跟着出現。

通過這種操作可以在一個函數內採用一些以前不容易實現的方式實現一些操作:

  • 將多重循環拉平
  • 將多級條件判斷變為循環判斷
  • 無需創建新的類就能快速生成一個上下文需要的特殊迭代算法

這相關的例子在《》中較多。

按照月老闆的名言:“業務複雜度是不會因為系統設計變化而減少的,它只是從一個地方轉移到了另外的地方。”,我們可以知道,這種寫法其實沒有使得原來就有的判斷和循環變少。只是改變了語法結構。

讀者可以將這種操作作為一種 “語法糖” 進行使用。如果是在團隊項目中,則需要尊重團隊成員的共同意見,因為這種操作並非所有人都願意接受。

當然,這種做法在一些地方會產生好處。例如在將本地函數、IEnumerable 和 Task 相結合的 T10 測試網絡連接 中。這種寫法就減少了傳統寫法中需要創建一個 List 或者 Array 的開銷。

總之,這種寫法,提供了一種新的思路。是否一定要使用,將取決於讀者團隊的接受程度。

異步迭代器

在 C# 8 和 .netcore 3.0 到來的版本中,我們迎接到了 IAsyncEnumerable 接口來實現異步迭代器的功能。

IEnumerable 是同步方法的迭代器,IAsyncEnumerable 可以看做是其異步版本。有了這個接口,那麼在迭代的過程中也可以充分利用 async/await 帶來的編程快感。

本系列中沒有添加這部分的示例,但是主體思路是一致的。

她的出現,只會使得開發者更容易應用以上總結的幾種主要場景。

詳細的例子,可以。

總結

本系列到此便結束了,希望讀者多在實踐中體會以上總結的幾種使用場景。

本系列中的例子已經全部使用  進行了重寫,讀者可以直接在本博客的頁面上運行這些示例。

如果無法正常的展示示例,讀者也可以。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

豐田出售部分特斯拉股份 將縮減電動車合作

豐田汽車已出售其所持特斯拉的部分股份,表明兩家公司將縮減在電動車開發方面的合作。雖然縮減電動車領域合作,但豐田計畫繼續與特斯拉在插電式混合動力車等領域合作。

豐田於2010年與特斯拉組成資本和業務合作關係,當時向特斯拉投資5000萬美元,截至今年3月31日豐田持有特斯拉約2.4%股份。豐田似乎已在市場上出售所持的部分特斯拉股份,出售比例未予披露。特斯拉的股價較豐田收購其股份時已躍升10倍以上。

特斯拉稍早時宣佈,將在今年內停止為豐田供應電動車使用的鋰電池。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

北汽新能源將推5款新車 全新品牌「眾創」4月發佈

近日, 北京新能源汽車股份有限公司黨委書記、總經理鄭剛表示,北汽新能源規劃推出5款全新車型,其中全新品牌“眾創”車型將於4月份正式發佈。即將推出的5款車型覆蓋A00級、A0級、A級轎車及A級 SUV 領域。

北汽新能源將推出5款全新車型,其中純電動A級車EU260(續航里程260公里)將於2-3月正式上市,接下來純電動A0級SUV—EX200(續航200公里)將於3-4月登陸市場,其後續推出的高里程版續航可達到300公里。在今年的9-10月份,旗下全新A00級“國民車”將上市,該車將進一步拉低入門售價;今年年底,全新“眾創”品牌旗下車型將上市,進一步豐富產品陣容。2017年,北汽新能源將推出全新D級高性能純電動轎車,其續航里程將超過500公里。

據悉,北汽新能源推動的“眾創”專案,將在4月舉行的北京車展上,正式發佈該品牌LOGO、車型及命名等細節。“眾創”品牌的首款車型將於今年底正式上市,該車將採用超輕量化的全鋁框架車身結構設計。據介紹,北汽新能源將彙集全球“電粉”,全面參與到新產品的名稱定義、標識設計甚至軟硬選裝件的創意開發中,打造全球首款眾創汽車,該項目也將作為北汽新能源“衛藍眾創”計畫一部分。

在北汽新能源的規劃中,在2017年將推出更具產品實力的全新高性能D級純電動轎車,新車也將成為北汽新能源的旗艦產品,其續航里程將達到500公里。從定位來看,該車將與特斯拉MODEL S處同一級別。按照此前消息,北汽新能源與Atieva聯合開展的高性能電動汽車設計研發工作正在矽谷緊鑼密鼓地推進之中,新車有望是該款D級轎車。

多款熱銷車型的出現將促使北汽新能源加快對於產業佈局的建設。據悉,北汽新能源將以“1(北京采育基地)+2(常州基地、青島基地)+I(北汽集團內部傳統乘用車生產基地)+P(社會合作夥伴生產資源)>80”為基礎,在十三五末期形成80萬輛以上生產能力,年產銷50萬輛規模,打造年營業收入600億元、上市市值1000億元的企業。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

python機器學習——邏輯回歸

我們知道感知器算法對於不能完全線性分割的數據是無能為力的,在這一篇將會介紹另一種非常有效的二分類模型——邏輯回歸。在分類任務中,它被廣泛使用

邏輯回歸是一個分類模型,在實現之前我們先介紹幾個概念:

幾率(odds ratio):
\[ \frac {p}{(1-p)} \]
其中p表示樣本為正例的概率,當然是我們來定義正例是什麼,比如我們要預測某種疾病的發生概率,那麼我們將患病的樣本記為正例,不患病的樣本記為負例。為了解釋清楚邏輯回歸的原理,我們先介紹幾個概念。

我們定義對數幾率函數(logit function)為:
\[ logit(p) = log \frac {p}{(1-p)} \]
對數幾率函數的自變量p取值範圍為0-1,通過函數將其轉化到整個實數範圍中,我們使用它來定義一個特徵值和對數幾率之間的線性關係為:
\[ logit(p(y=1|x)) = w_0x_0+w_1x_1+…+w_mx_m = \sum_i^nw_ix_i=w^Tx \]
在這裏,p(y=1|x)是某個樣本屬於類別1的條件概率。我們關心的是某個樣本屬於某個類別的概率,剛好是對數幾率函數的反函數,我們稱這個反函數為邏輯函數(logistics function),有時簡寫為sigmoid函數:
\[ \phi(z) = \frac{1}{1+e^{-z}} \]
其中z是權重向量w和輸入向量x的線性組合:
\[ z = w^Tx=w_0+w_1x_1+…+w_mx_m \]
現在我們畫出這個函數圖像:

import matplotlib.pyplot as plt
import numpy as np
def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))
z = np.arange(-7, 7, 0.1)
phi_z = sigmoid(z)
plt.plot(z, phi_z)
plt.axvline(0.0, color='k')
plt.axhspan(0.0, 1.0, facecolor='1.0', alpha=1.0, ls='dotted')
plt.yticks([0.0, 0.5, 1.0])
plt.ylim(-0.1, 1.1)
plt.xlabel('z')
plt.ylabel('$\phi (z)$')
plt.show()

可以看出當z接近於正無窮大時,函數值接近1,同樣當z接近於負無窮大時,函數值接近0。所以我們知道sigmoid函數將一個實數輸入轉化為一個範圍為0-1的一個輸出。

我們將邏輯函數將我們之前學過的Adaline聯繫起來,在Adaline中,我們的激活函數的函數值與輸入值相同,而在邏輯函數中,激活函數為sigmoid函數。

sigmoid函數的輸出被解釋為某個樣本屬於類別1的概率,用公式表示為:
\[ \hat y=\begin{cases}1,\quad \phi(z)\ge 0.5 \\\\0,\quad otherwise\end{cases} \]
也就是當函數值大於0.5時,表示某個樣本屬於類別1的概率大於0.5,於是我們就將此樣本預測為類別1,否則為類別0。我們仔細觀察上面的sigmoid函數圖像,上式也等價於:
\[ \hat y=\begin{cases}1,\quad z\ge 0.0 \\\\0,\quad otherwise\end{cases} \]
邏輯回歸的受歡迎之處就在於它可以預測發生某件事的概率,而不是預測這件事情是否發生。

我們已經介紹了邏輯回歸如何預測類別概率,接下來我們來看看邏輯回歸如何更新權重參數w

對於Adaline,我們的損失函數為:
\[ J(w) = \sum_i\frac12(\phi(z^{(i)})-y^{(i)})^2 \]
我們通過最小化這個損失函數來更新權重w。為了解釋我們如何得到邏輯回歸的損失函數,在構建邏輯回歸模型時我們要最大化似然L(假設數據集中的所有樣本都是互相獨立的):
\[ L(w)=P(y|x,w)=\prod^n_{i=1}P(y^{(i)}|x^{(i)};w)=\prod^n_{i=1}(\phi(z^{(i)}))^{y^{(i)}}(1-\phi(z^{(i)}))^{1-y^{(i)}} \]
通常我們會最大化L的log形式,我們稱之為對數似然函數
\[ l(w)=logL(w)=\sum_{i=1}^n\left[y^{(i)}log(\phi(z^{(i)})+(1-y^{(i)})log(1-\phi(z^{(i)}))\right] \]
這樣做有兩個好處,一是當似然很小時,取對數減小了数字下溢的可能性,二是取對數后將乘法轉化為了加法,可以更容易的得到函數的導數。現在我們可以使用一個梯度下降法來最大化對數似然函數,我們將上面的對數似然函數轉化為求最小值的損失函數J:
\[ J(w)=\sum_{i=1}^n\left[-y^{(i)}log(\phi(z^{(i)}))-(1-y^{(i)})log(1-\phi(z^{(i)}))\right] \]
為了更清晰的理解上式,我們假設對一個樣本計算它的損失函數:
\[ J(\phi(z),y;w)=-ylog(\phi(z))-(1-y)log(1-\phi(z)) \]
可以看出,當y=0時,式子的第一部分為0,當y=1時,式子的第二部分為0,也就是:
\[ J(\phi(z),y;w)=\begin{cases}-log(\phi(z)),\quad if\ y=1 \\\\-log(1-\phi(z)),\quad if \ y=0\end{cases} \]

可以看出,當我們預測樣本所屬於的類別時,當預測類別是樣本真實類別的概率越大時,損失越接近0,而當預測類別是真實類別的概率越小時,損失越接近無窮大。

作為舉例,我們這裏對權重向量w中的一個分量進行更新,首先我們求此分量的偏導數:
\[ \frac{\partial }{\partial w_j}l(w) = \left(y\frac{1}{\phi(z)}-(1-y)\frac{1}{1-\phi(z)}\right)\frac{\partial }{\partial w_j}\phi(z) \]
在繼續下去之前,我們先計算一下sigmoid函數的偏導數:
\[ \frac{\partial }{\partial z}\phi(z) = \frac{\partial }{\partial z}\frac{1}{1+e^{-z}}=\frac{1}{(1+e^{-z})^2}e^{-z}=\frac{1}{1+e^{-z}}(1-\frac{1}{1+e^{-z}})\\=\phi(z)(1-\phi(z)) \]
現在我們繼續:
\[ \left(y\frac{1}{\phi(z)}-(1-y)\frac{1}{1-\phi(z)}\right)\frac{\partial }{\partial w_j}\phi(z)\\=\left(y\frac{1}{\phi(z)}-(1-y)\frac{1}{1-\phi(z)}\right)\phi(z)(1-\phi(z))\frac{\partial }{\partial w_j}z\\=\left(y(1-\phi(z))-(1-y)\phi(z)\right)x_j\\=(y-\phi(z))x_j \]
所以我們的更新規則為:
\[ w_j = w_j + \eta\sum^n_{i=1}\left(y^{(i)}-\phi(z^{(i)})\right)x_j^{(i)} \]
因為我們要同時更新權重向量w的所有分量,所以我們更新規則為(此處w為向量):
\[ w = w+\Delta w\\\Delta w = \eta\nabla l(w) \]
因為最大化對數似然函數也就等價於最小化損失函數J,於是梯度下降更新規則為:
\[ \Delta w_j=-\eta\frac{\partial J}{\partial w_j}=\eta\sum^n_{i=1}\left(y^{(i)}-\phi(z^{(i)})\right)x^{(i)}_j\\w=w+\Delta w,\Delta w=-\eta \nabla J(w) \]

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

減少瓶裝水垃圾 日推出首款飲水機地圖app

摘錄自2020年2月24日公視新聞網報導

在日本有一款飲水地圖app,鼓勵人們帶環保杯,使用飲水機,還能幫用戶計算少用了多少寶特瓶、減了多少碳。

一家新創公司推出日本第一個飲水地圖,鼓勵人們只要帶著環保杯,想喝水時不要花錢,從地圖就近找到飲水台。這款app可以運算用戶裝水時的所有情況。Mymizu共同創辦人路易斯說:「我們四天前才把這個新的裝水追蹤器啟用,今天早上,我們追蹤到減少了約900個寶特瓶,我們今年的大目標就是達到100萬個。」

日本是全球塑膠包裝生產量第二大國,僅次於美國,根據聯合國資料,2014年日本每人平均使用塑膠資源超過30公斤。由於今年夏天將舉辦東京奧運,去年六月G20峰會便宣示減塑,並打算今年推出用錢換取回收塑膠袋的計畫。不過顯然這樣的app已經跑在政府前面,讓大家開始為了減塑而改變生活習慣,目前這款app在全球有30國在使用、下載次數超過三萬次。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

台灣寄大陸海運貨物規則及重量限制?

大陸寄台灣海運費用試算一覽表

南投搬家前需注意的眉眉角角,別等搬了再說!

發展電動車 港商E-Kong投資昶洧一億美元

香港上市公司E-Kong看好中國純電動車市場,決定大手筆投資美金一億元,與台灣電動車廠昶洧進行開發合作。

昶洧原為工具機、零件與馬達代工公司,近期開始轉型,以精密機械研發經驗打造自家純電動車品牌Thunder Power。Thunder Power結合綠能、物聯網等技術,未來也打算結合生物科技功能。昶洧表示,隨著台灣新政府即將走馬上任,「系統整合」也將成為未來的一大研發目標,希望能透過與政府、海外資金的合作,讓台灣品牌走入世界。

E-Kong本業為電訊服務與資訊科技,近年因應空氣品質惡化、電動車崛起,因此決定投資電動車產業,並選擇台灣昶洧做為合作夥伴。該筆投資將再一年間分三階段進行。第一階段將於今年2月19日前投資100萬美元,作為不可撤回的普通股股款;第二階段則投資5,000~5,500萬美元,時間在今年5月31日之前。第三階段則於年底前完成,投資額約4,500萬美元。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

台灣寄大陸海運貨物規則及重量限制?

大陸寄台灣海運費用試算一覽表

南投搬家前需注意的眉眉角角,別等搬了再說!

沃爾沃宣佈將在2017年推出首款無鑰匙汽車

日前,沃爾沃宣佈將在2017年出售配備全新數位鑰匙的汽車,未來車主可以使用手機上的數位鑰匙應用程式開啟汽車。屆時,沃爾沃將成為全世界首個提供無鑰匙汽車的廠商。

據悉,這個數字鑰匙將通過藍牙與車輛連接,需要使用者在車旁一定距離內才能使用,而且這種應用還有特定的使用者驗證程式,以防止車輛被他人以這種便捷方式偷竊。

說到全新沃爾沃數位鑰匙,它不僅可以實現實體鑰匙所能完成的上鎖、解鎖、發動引擎等一系列任務,還能借助手機應用將數位鑰匙分享給家人、朋友或急需使用沃爾沃汽車的人。也就是說,只要你願意,你可以讓任何人使用你的沃爾沃汽車。可以預見,數位鑰匙技術未來或將在汽車租賃市場得到廣泛應用。試想一下,當你到達目的地後,通過手機應用找到汽車、支付、解鎖、發動引擎,整個租賃過程一氣呵成,將徹底告別租車櫃檯前那令人頭疼的長隊。

關於沃爾沃創新數位鑰匙技術的其他資訊,將在2016年度的巴賽隆納世界移動通信大會上的愛立信展臺進行首次亮相。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

台灣寄大陸海運貨物規則及重量限制?

大陸寄台灣海運費用試算一覽表

南投搬家前需注意的眉眉角角,別等搬了再說!