Gremlin-Python 线程池耗尽

Gremlin-python在链接远程JanusGraph的时候, 如果提交了一个复杂的查询, 远程JG会抛出异常错误, 这时Client中线程池中的Connection对象会捕捉该异常, 但是在3.2.6版本中不会将该Connection对象重新放回到线程池中, 这会导致经过多次类似复杂查询后, Client中线程池会被耗尽.
这个问题已经在 https://github.com/apache/tinkerpop/commit/6b51c54f67419039dc114406c1d61918b2ccf39f 中被修复了, 并且在3.4版本中进行了发布.

AttributeError: ‘SVC’ object has no attribute ‘_impl’

[Sklearn error: ‘SVR’ object has no attribute ‘_impl’] (https://stackoverflow.com/questions/19089386/sklearn-error-svr-object-has-no-attribute-impl)
0.14.1 SVC issue

Mostly, this error is caused by improper version of scikit-learn.
For example, you trained one model using scikit-learn with version of 0.1. However, you updated your packages to 0.2 after training.
When you using joblib.load to load the model file, it will throws out this error since pickle can’t recognize which version.

AttributeError: Can’t pickle local object ‘SOME_OBJECT_OR_FUNC’

I met this error when I tried to use GridSearchCV to find best hyper-parameter.
I checked my configuration and found when I set the parameter n_jobs to -1 which means run parallelly, it will throw out this error.

gs = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=1, cv=cv)

It seems that when using multiprocessing to run estimator parallely, the pickle cannot serialize the object(e.g nested function).

Can’t pickle nested function in FeatureExtracter
Python: pickling nested functions

机器学习中的概念总结

Cliff & Flat Regions

Cliff and Flat regions in the parameter space of neural network
Cliff and Flat regions in the parameter space of neural network.
θ and γ are two parameters and L(θ, γ) is the loss function. The region with light color is a cliff and the region with dark color is a flat region.

Whiten

在深度学习训练中,白化(Whiten)是加速收敛的一个小Trick,所谓白化是指将图像像素点变化到均值为0,方差为1的正态分布。

Dropout

Batch Normalization

Weight Normalization

Local Minima

局部最小值

Ablation Study

2
消融研究,指通过移除某个模型或者算法的某些特征,来观察这些特征对模型效果的影响
实际上ablation study就是为了研究模型中所提出的一些结构是否有效而设计的实验。比如你提出了某某结构,但是要想确定这个结构是否有利于最终的效果,那就要将去掉该结构的网络与加上该结构的网络所得到的结果进行对比,这就是ablation study

An ablation study typically refers to removing some “feature” of the model or algorithm, and seeing how that affects performance.

Examples:

An LSTM has 4 gates: feature, input, output, forget. We might ask: are all 4 necessary? What if I remove one? Indeed, lots of experimentation has gone into LSTM variants, the GRU being a notable example (which is simpler).
If certain tricks are used to get an algorithm to work, it’s useful to know whether the algorithm is robust to removing these tricks. For example, DeepMind’s original DQN paper reports using (1) only periodically updating the reference network and (2) using a replay buffer rather than updating online. It’s very useful for the research community to know that both these tricks are necessary, in order to build on top of these results.
If an algorithm is a modification of a previous work, and has multiple differences, researchers want to know what the key difference is.
Simpler is better (inductive prior towards simpler model classes). If you can get the same performance with two models, prefer the simpler one.

Sharp Minimal

尖锐最小值
Yoshua Bengio 组在2017年发表文章Sharp Minima Can Generalize For Deep Nets介绍了在深度神经网络中尖锐的最小值也可以让模型具有泛化能力.

Internal Covariate Shift – ICS

Python中的继承机制 MRO 和 C3

继承关系顺序的不同

通过一个例子来说明继承关系顺序的不同.
在两端代码中, 分别使用显示调用父类初始化方法和super机制进行初始化操作.
继承关系图
显示调用父类初始化方法

class A:
    def __init__(self):
        print("Enter A")
        print("Leave A")

class B(A):
    def __init__(self):
        print("Enter B")
        A.__init__(self)
        print("Leave B")

class C(A):
    def __init__(self):
        print("Enter C")
        A.__init__(self)
        print("Leave C")

class D(A):
    def __init__(self):
        print("Enter D")
        A.__init__(self)
        print("Leave D")

class E(B, C, D):
    def __init__(self):
        print("Enter E")
        B.__init__(self)
        C.__init__(self)
        D.__init__(self)
        print("Leave E")

E()

运行上述代码, 得到的输出是

Enter E
Enter B
Enter A
Leave A
Leave B
Enter C
Enter A
Leave A
Leave C
Enter D
Enter A
Leave A
Leave D
Leave E

super机制

class A:
    def __init__(self):
        print("Enter A")
        print("Leave A")

class B(A):
    def __init__(self):
        print("Enter B")
        super(B, self).__init__()
        print("Leave B")

class C(A):
    def __init__(self):
        print("Enter C")
        super(C, self).__init__()
        print("Leave C")

class D(A):
    def __init__(self):
        print("Enter D")
        super(D, self).__init__()
        print("Leave D")

class E(B, C, D):
    def __init__(self):
        print("Enter E")
        super(E, self).__init__()
        print("Leave E")

E()

运行上述代码, 得到的输出是

Enter E
Enter B
Enter C
Enter D
Enter A
Leave A
Leave D
Leave C
Leave B
Leave E

由上述的例子可以得出, 在涉及多继承时, super机制可以保证公共父类仅被执行一次,至于执行的顺序,是按照mro进行的(E.mro).

Python 多重继承mro
文档 : The Python 2.3 Method Resolution Order
Python Tutorial: Understanding Python MRO – Class search path

什么是MRO?

MROmethod resolution order,主要用于在多继承时判断调的属性的路径(来自于哪个类). 在Python2.3之前是基于此算法,但从Python2.3起应用了新算法:C3算法。

为什么采用C3算法

C3算法最早被提出是用于Lisp的,应用在Python中是为了解决原来基于深度优先搜索算法不满足本地优先级,和单调性的问题。
本地优先级:指声明时父类的顺序,比如C(A,B),如果访问C类对象属性时,应该根据声明顺序,优先查找A类,然后再查找B类。
单调性:如果在C的解析顺序中,A排在B的前面,那么在C的所有子类里,也必须满足这个顺序。
在Python官网的The Python 2.3 Method Resolution Order中作者举了例子,说明这一情况。

F=type(‘Food’, (), {remember2buy:’spam’})
E=type(‘Eggs’, (F,), {remember2buy:’eggs’})
G=type(‘GoodFood’, (F,E), {})
根据本地优先级在调用G类对象属性时应该优先查找F类,而在Python2.3之前的算法给出的顺序是G E F O,而在心得C3算法中通过阻止类层次不清晰的声明来解决这一问题,以上声明在C3算法中就是非法的。

C3算法

判断mro要先确定一个线性序列,然后查找路径由由序列中类的顺序决定。所以C3算法就是生成一个线性序列。
如果继承至一个基类:
class B(A)
这时B的mro序列为[B,A]

如果继承至多个基类
class B(A1,A2,A3 …)
这时B的mro序列 mro(B) = [B] + merge(mro(A1), mro(A2), mro(A3) …, [A1,A2,A3])
merge操作就是C3算法的核心。
遍历执行merge操作的序列,如果一个序列的第一个元素,在其他序列中也是第一个元素,或不在其他序列出现,则从所有执行merge操作序列中删除这个元素,合并到当前的mro中。
merge操作后的序列,继续执行merge操作,直到merge操作的序列为空。
如果merge操作的序列无法为空,则说明不合法。

例子:
class A(O):pass
class B(O):pass
class C(O):pass
class E(A,B):pass
class F(B,C):pass
class G(E,F):pass

A、B、C都继承至一个基类,所以mro序列依次为[A,O]、[B,O]、[C,O]
mro(E) = [E] + merge(mro(A), mro(B), [A,B])
= [E] + merge([A,O], [B,O], [A,B])
执行merge操作的序列为[A,O]、[B,O]、[A,B]
A是序列[A,O]中的第一个元素,在序列[B,O]中不出现,在序列[A,B]中也是第一个元素,所以从执行merge操作的序列([A,O]、[B,O]、[A,B])中删除A,合并到当前mro,[E]中。
mro(E) = [E,A] + merge([O], [B,O], [B])
再执行merge操作,O是序列[O]中的第一个元素,但O在序列[B,O]中出现并且不是其中第一个元素。继续查看[B,O]的第一个元素B,B满足条件,所以从执行merge操作的序列中删除B,合并到[E, A]中。
mro(E) = [E,A,B] + merge([O], [O])
= [E,A,B,O]

同理
mro(F) = [F] + merge(mro(B), mro(C), [B,C])
= [F] + merge([B,O], [C,O], [B,C])
= [F,B] + merge([O], [C,O], [C])
= [F,B,C] + merge([O], [O])
= [F,B,C,O]

mro(G) = [G] + merge(mro[E], mro[F], [E,F])
= [G] + merge([E,A,B,O], [F,B,C,O], [E,F])
= [G,E] + merge([A,B,O], [F,B,C,O], [F])
= [G,E,A] + merge([B,O], [F,B,C,O], [F])
= [G,E,A,F] + merge([B,O], [B,C,O])
= [G,E,A,F,B] + merge([O], [C,O])
= [G,E,A,F,B,C] + merge([O], [O])
= [G,E,A,F,B,C,O]

Learning to Rank : From Pairwise Approach to Listwise Approach

ListNet

Paper

Learning to Rank: From Pairwise Approach to Listwise Approach

开源实现

  1. Minorthird
    由CMU的教授William W. Cohen带领他的学生们做的,类似于Weka,是一个实现了大量机器学习、数据挖掘算法的开源工具,它在Github上的主页Minorthird – Github.
  2. 另一个是罗磊同学近期做的,使用的是单层神经网络模型来调整权值。目前已经在Google code上开源,地址在这儿。欢迎大家使用并给出意见。

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能使哪些向量(特征向量)只发生拉伸,而拉伸的程度,自然就是特征值λλ了。