pyhanlp重载自定义词典

在使用hanlp的过程中可能会遇到需要重新加载词典的方法,
在HanLP的Git Issue中有相关问题的解答
如何在程序中重新加载自定义的词典
hanlp 能否加入一个远程词典更新的功能

hanlp 能否加入一个远程词典更新的功能中提到了一个接口 自定义词典支持热更新

CustomDictionary.java提供了一个reload方法,

    /**
     * 热更新(重新加载)<br>
     * 集群环境(或其他IOAdapter)需要自行删除缓存文件(路径 = HanLP.Config.CustomDictionaryPath[0] + Predefine.BIN_EXT)
     * @return 是否加载成功
     */
    public static boolean reload()
    {
        String path[] = HanLP.Config.CustomDictionaryPath; // Tips : 读取HanLP的配置文件中CustomDIctionaryPath
        if (path == null || path.length == 0) return false;
        new File(path[0] + Predefine.BIN_EXT).delete(); // 删掉缓存
        return loadMainDictionary(path[0]);
    }

其中调用了loadMainDictionary方法.

private static boolean loadMainDictionary(String mainPath)
     {
         logger.info("自定义词典开始加载:" + mainPath);
         if (loadDat(mainPath)) return true;
         dat = new DoubleArrayTrie<CoreDictionary.Attribute>();
         TreeMap<String, CoreDictionary.Attribute> map = new TreeMap<String, CoreDictionary.Attribute>();
        LinkedHashSet<Nature> customNatureCollector = new LinkedHashSet<Nature>();
        try
        {
            String path[] = HanLP.Config.CustomDictionaryPath;
            for (String p : path)
            {
                Nature defaultNature = Nature.n;
                 int cut = p.indexOf(' ');
                 if (cut > 0)
                 {
                     // 有默认词性
                     String nature = p.substring(cut + 1);
                     p = p.substring(0, cut);
                     try
                     {
                         defaultNature = LexiconUtility.convertStringToNature(nature, customNatureCollector);
                     }
                     catch (Exception e)
                     {
                         logger.severe("配置文件【" + p + "】写错了!" + e);
                         continue;
                     }
                 }
                 logger.info("以默认词性[" + defaultNature + "]加载自定义词典" + p + "中……");
                 boolean success = load(p, defaultNature, map, customNatureCollector);
                 if (!success) logger.warning("失败:" + p);
             }
             if (map.size() == 0)
             {
                 logger.warning("没有加载到任何词条");
                 map.put(Predefine.TAG_OTHER, null);     // 当作空白占位符
             }
             logger.info("正在构建DoubleArrayTrie……");
             dat.build(map);
             // 缓存成dat文件,下次加载会快很多
             logger.info("正在缓存词典为dat文件……");
             // 缓存值文件
             List<CoreDictionary.Attribute> attributeList = new LinkedList<CoreDictionary.Attribute>();
             for (Map.Entry<String, CoreDictionary.Attribute> entry : map.entrySet())
             {
                 attributeList.add(entry.getValue());
             }
             DataOutputStream out = new DataOutputStream(IOUtil.newOutputStream(mainPath + Predefine.BIN_EXT));
             // 缓存用户词性
             IOUtil.writeCustomNature(out, customNatureCollector);
             // 缓存正文
             out.writeInt(attributeList.size());
             for (CoreDictionary.Attribute attribute : attributeList)
             {
                 attribute.save(out);
             }
             dat.save(out);
             out.close();
         }
         catch (FileNotFoundException e)
         {
             logger.severe("自定义词典" + mainPath + "不存在!" + e);
             return false;
         }
         catch (IOException e)
         {
             logger.severe("自定义词典" + mainPath + "读取错误!" + e);
             return false;
         }
         catch (Exception e)
         {
             logger.warning("自定义词典" + mainPath + "缓存失败!\n" + TextUtility.exceptionToString(e));
         }
         return true;
     }

其中loadDat

/**
      * 从磁盘加载双数组
      *
      * @param path
      * @return
      */
static boolean loadDat(String path)
     {
         try
         {
             ByteArray byteArray = ByteArray.createByteArray(path + Predefine.BIN_EXT);
             if (byteArray == null) return false;
             int size = byteArray.nextInt();
             if (size < 0)   // 一种兼容措施,当size小于零表示文件头部储存了-size个用户词性
             {
                 while (++size <= 0)
                 {
                     Nature.create(byteArray.nextString());
                 }
                 size = byteArray.nextInt();
             }
             CoreDictionary.Attribute[] attributes = new CoreDictionary.Attribute[size];
             final Nature[] natureIndexArray = Nature.values();
             for (int i = 0; i < size; ++i)
             {
                 // 第一个是全部频次,第二个是词性个数
                 int currentTotalFrequency = byteArray.nextInt();
                 int length = byteArray.nextInt();
                 attributes[i] = new CoreDictionary.Attribute(length);
                 attributes[i].totalFrequency = currentTotalFrequency;
                 for (int j = 0; j < length; ++j)
                 {
                     attributes[i].nature[j] = natureIndexArray[byteArray.nextInt()];
                     attributes[i].frequency[j] = byteArray.nextInt();
                 }
             }
             if (!dat.load(byteArray, attributes)) return false;
         }
         catch (Exception e)
         {
             logger.warning("读取失败,问题发生在" + TextUtility.exceptionToString(e));
             return false;
         }
         return true;
     }

可以看出 reload方法必须先删除bin文件, 再重新生成.

在Python中可以按照下面的方法进行重载

custom_dictionary = JClass('com.hankcs.hanlp.dictionary.CustomDictionary')
custom_dictionary.reload()

调用CustomDictionary类的reload方法可以重新按照properties中的设置重构一个bin文件.

同时也提供了直接添加自定义字典的方法, 但是看起来并不是很合适, 因为这样会

HanLP.Config. CustomDictionaryPath = ["dict.txt","dict2.txt"]
CustomDictionary.reload()

pyhanlp重加载词典中可能会遇到的问题

在进行重加载过程中发现一个可能存在的问题, 已经提交 reload生成自定义词典bin文件在重新载入时抛出异常ArrayIndexOutOfBoundsException
该问题主要表现为reload生成的bin文件在下次启动时不能被正常识别, 会抛出ArrayIndexOutOfBoundsException的问题.
原因是因为reload过程中, 静态类已经及存储了在这一次启动过程中识别过的自定义词性, 但是reload中没有写入这些词性, 导致在第二次启动过程中, 会发生越界.

矩阵的分解

矩阵分解 (decomposition, factorization)是将矩阵拆解为数个矩阵的乘积,可分为三角分解、满秩分解、QR分解、Jordan分解和SVD(奇异值)分解等,常见的有三种:1)三角分解法 (Triangular Factorization),2)QR 分解法 (QR Factorization),3)奇异值分解法 (Singular Value Decomposition)。

矩阵常见分解法:
1. LU factorization(LU分解法)
LU 分解法
2. QR factorization
矩阵的QR分解(三种方法)Python实现
Gram-Schmidt正交化
Givens矩阵与Givens变换
Householder矩阵与Householder变换

#coding:utf8
import numpy as np

def gram_schmidt(A):
    """Gram-schmidt正交化"""
    Q=np.zeros_like(A)
    cnt = 0
    for a in A.T:
        u = np.copy(a)
        for i in range(0, cnt):
            u -= np.dot(np.dot(Q[:, i].T, a), Q[:, i]) # 减去待求向量在以求向量上的投影
        e = u / np.linalg.norm(u)  # 归一化
        Q[:, cnt] = e
        cnt += 1
    R = np.dot(Q.T, A)
    return (Q, R)

def givens_rotation(A):
    """Givens变换"""
    (r, c) = np.shape(A)
    Q = np.identity(r)
    R = np.copy(A)
    (rows, cols) = np.tril_indices(r, -1, c)
    for (row, col) in zip(rows, cols):
        if R[row, col] != 0:  # R[row, col]=0则c=1,s=0,R、Q不变
            r_ = np.hypot(R[col, col], R[row, col])  # d
            c = R[col, col]/r_
            s = -R[row, col]/r_
            G = np.identity(r)
            G[[col, row], [col, row]] = c
            G[row, col] = s
            G[col, row] = -s
            R = np.dot(G, R)  # R=G(n-1,n)*...*G(2n)*...*G(23,1n)*...*G(12)*A
            Q = np.dot(Q, G.T)  # Q=G(n-1,n).T*...*G(2n).T*...*G(23,1n).T*...*G(12).T
    return (Q, R)

def householder_reflection(A):
    """Householder变换"""
    (r, c) = np.shape(A)
    Q = np.identity(r)
    R = np.copy(A)
    for cnt in range(r - 1):
        x = R[cnt:, cnt]
        e = np.zeros_like(x)
        e[0] = np.linalg.norm(x)
        u = x - e
        v = u / np.linalg.norm(u)
        Q_cnt = np.identity(r)
        Q_cnt[cnt:, cnt:] -= 2.0 * np.outer(v, v)
        R = np.dot(Q_cnt, R)  # R=H(n-1)*...*H(2)*H(1)*A
        Q = np.dot(Q, Q_cnt)  # Q=H(n-1)*...*H(2)*H(1)  H为自逆矩阵
    return (Q, R)

np.set_printoptions(precision=4, suppress=True)
A = np.array([[6, 5, 0],[5, -1, 4],[5, 1, -14],[0, 4, 3]],dtype=float)

(Q, R) = gram_schmidt(A)
print(Q)
print(R)
print np.dot(Q,R)

(Q, R) = givens_rotation(A)
print(Q)
print(R)
print np.dot(Q,R)

(Q, R) = householder_reflection(A)
print(Q)
print(R)
print np.dot(Q,R)
  1. Jordan factorization
  2. SVD factorization

https://blog.csdn.net/bitcarmanlee/article/details/52662518

https://blog.csdn.net/bitcarmanlee/article/details/52068118
这篇文章里讲到了特征值的作用.
特征值的定义很简单:Ax=λxAx=λx。其中AA为矩阵,λλ为特征值,xx为特征向量。不知道大家想过没有:为什么一个向量,跟一个数相乘的效果,与跟一个矩阵的效果相乘是一样的呢?
这得用到我们先前的结论:矩阵是线性空间里的变换的描述。矩阵AA与向量相乘,本质上对向量xx进行一次线性转换(旋转或拉伸),而该转换的效果为常数cc乘以向量xx(即只进行拉伸)。当我们求特征值与特征向量的时候,就是为了求矩阵AA能使哪些向量(特征向量)只发生拉伸,而拉伸的程度,自然就是特征值λλ了。

第十二章 随机过程及其统计描述

P 300 – 316 正文
P 317 – 318 习题

1. 随机过程的概念

随机过程的研究对象是随时间演变的随机现象, 对于这种现象, 不能用随机变量或者多维随机变量来合理的表达, 而需要用一族(无限多个)随机变量来描述.

随机过程
T 是一无限实数集, 将依赖于参数 t \in T的一族(无限多个)随机变量称为随机过程, 记为 \lbrace X(t), t \in T \rbrace.
每一个 t \in T, X(t)是一随机变量, T 叫做参数集.
常把 t 看做时间, 称 X(t) 为时刻 t 时过程的状态. 而 X(t_1)=x(实数)说成是 t=t_1时过程处于状态 x.
对于一切 t \in T, X(t) 所有可能取的一切值的全体称为随机过程的状态空间.

对随机过程XXX进行一次试验(即在T上进行一次全程观测), 其结果是t的函数, 称它为随机过程的一个样本函数或样本曲线.

伯努利过程伯努利随机序列
抛掷一颗筛子, 设 X_n 是第 n 次(n\ge1)抛掷的点数, 对于 n = 1, 2,\cdots 的不同值, X_n 是不同的随机变量, 因而 \lbrace X_n, ,n \ge 1\rbrace构成一随机过程, 称为 伯努利过程伯努利随机序列

2. 随机过程的统计描述

2.1 随机过程的分布函数族

一维分布函数
一维分布函数族

n维分布函数族
有限维分布函数族

2.2 随机过程的数字特征

均值函数

集平均 或 统计平均 (需和14章中的时间平均区分)

均方差函数 和 方差函数

标准差函数

自相关函数, 简称相关函数.

自相关函数和自协方差函数是刻画随机过程自身在两个不同时刻的状态之间统计依赖关机的数字特征.

第八章 假设检验

P 178 ~ 218 正文
P 218 ~ 223 习题

1. 假设检验

显著性水平

检验统计量
原假设/零假设

备择假设

拒绝域 临界点

显著性校验

双边备择假设 双边假设检验

处理参数的假设检验步骤如下:
1.
2.
3.
4.
5.

2. 正态总体均值的假设检验

2.1 单个总体$均值\niu$的检验

2.2 两个正态总体均差值的检验(t检验)

2.3 基于成对数据的检验(t检验)

3. 正态总体方差的假设检验

3.1 单个总体的情况

3.2 两个总体的情况

4. 置信区间和假设检验之间的关系

5. 样本容量的选取

6. 分布拟合检验

6.1 单个分布的 \Chi^2 拟合检验法

6.2 分布族的 \Chi^2拟合检验

6.3 偏度, 峰度 检验

7. 秩和检验