CuriousY A world with wonder

Some notes for Golang

| Comment
  1. $GOPATH为工作目录,它有点类似于Python的site-packages,里面用于存放项目依赖的第三方代码。

    $GOPATH
    ├── pkg
    └── src
    		├── golang.org
    		└── Example-project
    
  2. 对于某一个项目,代码一般不要放在src目录下,而是按照功能放置代码文件(比如和web相关的就放在web文件夹内)。这一点和$GOPATH不能混淆(refer)。

  3. 使用go get安装依赖之前需要使用go mod init来初始化。

  4. 不同于JavaScript的NPM把依赖安装在各个项目目录下,Python可以通过virtualenv来隔离项目依赖,Go把所有的依赖都存放在工作目录下(这里只说Go官方支持的modules方式)。在每个项目下,使用go.modgo.sum文件来标识项目所需的依赖,对于同一个依赖,会根据其version和commit来放置到不同的文件夹。因此,同一个依赖在$GOPATH下可能会有很多份不同版本的代码。(这样虽然可能复用之前安装过的依赖,但如何有效地清除那些已经不再需要的依赖呢?)

  5. 对比Python:

    • Go没有set数据结构(原因么看这里),要实现一个集合可以这样:

      var mySet = map[string]bool {}
      mySet["a"] = true   // Add "a"
      _, ok = mySet["b"]  // Check if "b" exist
      
    • Go不支持方法定义时给参数默认值(by design,see here), 看到的比较好的替代方案是这样的:

      type Params struct {
        a, b, c int
      }
           
      func doIt(p Params) int {
        return p.a + p.b + p.c 
      }
           
      // you can call it without specifying all parameters
      doIt(Params{a: 1, c: 9})
      
  6. Go的module用init函数可以做一些初始化的工作,而struct的并不存在初始化的特殊函数,如果想要在struct生成时做些初始化操作,可以使用一个额外的工厂函数来生成该struct:

    type Person struct {
      name string
      age  int
    }
       
    func NewPerson(name string, age int) {
      // Do some init work
      return &Person{name, age}
    }
    
  7. Go标准库对xml或json的解析都需要提前定义好它的format,比如说对于xml:

    var body = `<response>
     <sessionKey>exuOpep4^t^oJRtf1JCAfnlIAhDK76ZlMTwP6uWZwXebJ_NRsh8qUBUpxM7LNtOi8Tt6J9DUvdcQdb1r9y8a^V</sessionKey>
      <messages>
        <msg code=""></msg>
      </messages>
    </response>
    `
    // Define the format
    var xmlBody struct {
      SessionKey string `xml:"sessionKey"`
    }
    if err := xml.Unmarshal([]byte(body), &xmlBody); err != nil {
      log.Fatal(err)
    }
    // Parsing result
    fmt.Println(xmlBody.SessionKey)
    
Read more

关于Typescript以及React

| Comment
  1. 使用Webstorm(或vscode)可以debug Typescript的代码,但需要在tsconfig.json中把sourceMap设为true。然后在Typescript代码中打上断点,再使用Node.js来debug编译过的JavaScript代码(编译过后应该同时会生成一个.map文件,根据这个文件来映射.js.ts进行debug)。

  2. tsconfig.json中可以设置outDir,即为编译后的.js文件会放到指定的目录下。

  3. tsconfig.json的compilerOptions中的lib如果包含了dom,则Typescript会认为这是一个前端的JS环境 ,所以一些保留字也会和前端JS的一致,这时候再用Node.js的话是有一些问题的,比如说下面的代码编译就没法通过:

    const fetch = require("node-fetch");
    

    错误信息为redeclared variable,因为它认为Global变量中已经有一个叫做fetch的变量了(但Node.js中并没有~)。

    比较好的解决方法是分别使用不同的tsconfig.jsontslint.json来维护前端代码和Node.js的后端代码。

  4. tslint默认配置要求interface的命名必须以I开头,但我看了许多人不建议这么命名,所以,可以把interface相关的rule修改下。

  5. 可以使用ReturnType来使用一个函数的返回值的类型定义新的类型:

    type yyy = ReturnType<typeof xxx>;
    
  6. create-react-app目前支持生成typscript的项目,但tslint目前要手动创建,并且在package.json中添加如下的命令:

    "lint": "tslint -c tslint.json src/**/*.{ts,tsx}"
    
  7. 可以安装额外的tslint package来扩充规则,对于React项目而言,比较有用的是tslint-react,贴下我的tslint.json作为参考:

    {
      "extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"],
      "linterOptions": {
        "exclude": [
          "config/**/*.js",
          "node_modules/**/*.ts",
          "coverage/lcov-report/*.js"
        ]
      },
      "rules": {
        "no-console": false,
        "semicolon": false,
        "ordered-imports": false,
        "object-literal-sort-keys": false,
        "variable-name": [true, "allow-leading-underscore", "allow-pascal-case"],
        "interface-name": [true, "never-prefix"],
        "jsx-boolean-value": false,
        "max-classes-per-file": false
      }
    }
    
  8. 很多example可以参考https://github.com/piotrwitek/react-redux-typescript-guide

  9. React现在不建议在组件中使用匿名函数,原因是某些情况下会导致性能下降。所以,要获取组件中某个JSX的ref可以这样做:

    class MyComponent extends React.Component<Props, State> {
        private elem: React.RefObject<HTMLDivElement> = React.createRef<HTMLDivElement>();
        public render() {
            return (
                <div ref={this.elem}>
                    xxx
                <div/>
            );
        }
    }
    

Reference

About Deep Q Network

| Comment
  1. DQN是Q learning和Neural network结合的产物,而Q learning是强化学习经典的算法。Q learning的思路其实不复杂:假设在某一步骤时有N个选择,根据状态表(关于这个概念后面会再聊),选择了option A,进入到下一个步骤,继续根据状态表选择了option B,以此类推,直到最后一步的选择得到了结果,如果这个结果正是我们想得到的,则我们会给予一个奖励,怎么个奖励法?其实就是更新状态表,把之前的各个状态下对应的选择的权重进行提升。否则,如果结果不是我们想要的,则进行惩罚:当然也是更新状态表。所谓状态表,是这样一张表:它的每行都是一个状态,每列是对应该状态的各种选择,表中的值则是选择所对应的权重(也可以理解为做各个选择所期望得到的奖励):

      option A option B option C option D
    状态一 -0.12 0.28 0 0
    状态二 0.33 0 -0.21 0
    状态三 0 -0.02 0.31 0.54

    这里的状态根据所解决的问题不同而不同,比如说走迷宫的问题,那么走迷宫的人的坐标就是状态,而上下左右四个方向则是选项。需要注意的是,并不是每个状态下都会对应所有的选项的,比如在迷宫底部,向下这个选项就是不存在的(或者说它的权重无限低)。

  2. Q learning中的Q是Quality的缩写,就是状态表中的权重也可以认为是Q值(选择的质量)。

  3. Q learning如果仅仅在最后一步达到目标后再去更新状态表,其实是有一点不合适的,假设我们一共做了n次选择,我们当然可以给第n次选择奖励1,第n-1次选择奖励0.9,以此类推。但是这样往往最终得到的并不是最优解,可能我们可以少做几次选择也能达到目标,但由于这种奖励方式,导致多余的那几次选择都得到了奖励。所以,实际的Q learning是这样“发放奖励”的:

    • 如果当前的选择之后的状态即是我们所想要达到的目标状态,那么我们直接给予该选择一个固定的奖励;
    • 如果当前的选择之后的状态并非是最终的目标状态,那么我们使用该选择之后的状态下的所有选项中的最高权重(事实上这个拥有最高权重的选项就是下一步要做的选择),以一定比例给予当前的选择。

    所以,Q learning是每一次选择结束都会去更新状态表的。

  4. Q learning训练的过程就是一个不断去更新状态表的过程,做预测时,状态表就是决策表。对于复杂的问题,Q learning有这么些缺点:

    • 状态难以定义
    • 状态表存储成本高
    • “奖惩”力度难以把控

    并且,为了学习解决一个问题需要数以万次的尝试,这离人类的智能相差很远,这也是目前强化学习(不仅是Q learning)被人所病垢比较多的一点。

  5. 试想下,在Q learning的算法中,神经网络可以做些什么?首先,做选择可以交由神经网络来做,假设我们一共有N种选择,构造一个输出层含有N个神经元的网络即可,它的输入当然就是当前时刻的状态,这样我们就不用再创建上面那张状态表了;其次,“奖惩”力度也可以由神经网络来决定,它的输入为下一时刻的状态(即做出当前选择之后的状态),输出为预测的下一个状态下各个选择的权重(然后同样用预测的之后的所有选择的最高权重来给予奖励)。前者在DQN中称之为evaluate network,后者为target network。

Read more

About Recurrent Neural Network

| Comment
  1. RNN和普通的神经网络的区别在于,它中间隐层的状态不仅取决于输入值,还取决于隐层之前的状态值。即对于普通的神经网络,S = f(W * X + b),其中S为隐层的状态值(可以理解为隐层神经元的输出),X为输入向量,W为隐层神经元的权重,b为偏置,f为激活函数;而对于RNN,S1 = f(W * X + b + W_s * S0),其中,S1为隐层当前的状态值,S0为隐层上一个时刻的状态值,W_s为权重。

    RNNs are called recurrent because they perform the same task for every element of a sequence, with the output being depended on the previous computations.

  2. 因为RNN隐层上一时刻的状态值又包含了上上一时刻的状态值,所以理论上,某一时刻隐层的状态值是有其之前的所有时刻的状态值共同决定的。假设W_s是小于1的权重,那么越靠近当前时刻的状态对目前状态的影响越大,这也比较符合我们的经验认知。

    In theory RNNs can make use of information in arbitrarily long sequences, but in practice they are limited to looking back only a few steps (more on this later).

  3. 如果我们把RNN中循环的网络展开来,其实很像含有多个隐层的普通神经网络,只不过每个隐层都多了一个外部输入,且所有隐层共享了权重和偏置(这一点不禁联想到CNN)。在某种程度上来说,RNN和CNN都可以算是DNN的特例,所以也不要奇怪有些事情不光CNN能做,RNN也能实现(甚至DNN也可以做到)。比如说这个代码就实现了使用RNN来对MNIST手写数字进行分类,它把图像的每一行像素作为输入,输入到一个循环了N(N为图像高度)次的RNN中,最后用一个全连接层作为输出。 A recurrent neural network and the unfolding in time of the computation involved in its forward computation.

Read more

About Generative Adversarial Networks

| Comment
  1. 从网络结构上来说,Generative Adversarial Networks(GAN)并没有创造什么全新的东西。以生成图像为例,GAN其实就是一个反向的CNN用于生成图片加上一个DNN用于决策判别。

  2. GAN的训练过程比较微妙,因为它涉及到两个相对独立的神经网络的训练:生成网络(generator)的训练依赖于判别网络(discriminator)的输出,而判别网络的训练又依赖于生成网络的输出。感觉似乎是一个死循环,但实际上却是可以训练的,具体过程如下:

    Step 1) Set the discriminator trainable

    Step 2) Train the discriminator with the real MNIST digit images and the images generated by the generator to classify the real and fake images.

    Step 3) Set the discriminator non-trainable

    Step 4) Train the generator as part of the GAN. We feed latent samples into the GAN and let the generator to produce digit images and use the discriminator to classify the image.

    在上述四个步骤完成之后(即步骤2训练的判别网络和步骤4训练的生成网络的损失都达到了比较小的值),需要再回到步骤1,以此反复地训练判别网络和生成网络。最终的目标是无论如何训练判别网络,它都很难区分出生成网络的输出和真实的样本(即任意输入一个真实样本或生成网络的输出,判别网络输出它的真实概率均为0.5(概率范围为0到1))。 所谓“Set the discriminator non-trainable”是指反向传播时不改变判别网络中的参数(weights和bias),即训练生成网络就只改变生成网络中的参数,保持判别网络中参数不变,反之训练判别网络时就只改变判别网络中的参数,保持生成网络中的参数不变。

  3. 对于生成图像的反向CNN,它是如何从少量的输入扩展到一张高分辨率的图片的呢?因为是反向的,卷积层其实也要“反”过来,具体做法以输入为4x4的图像为例,假设正向卷积时选取的是3x3的卷积核,且没有填补图像边缘,那么卷积完成后得到的是一个2x2的矩阵;反过来要从一个2x2的矩阵得到一幅4x4的图像/矩阵,如果也是使用3x3的卷积核,就需要对2x2的矩阵边缘做填补(一般直接填0),填补到一个6x6的矩阵,再做卷积就得到了4x4的矩阵(如下图所示)。这样的卷积层叫做transposed convolutional layer。 transposed convolutional layer

Read more
| Page 3 of 25 |