• 从 Numpy+Pytorch 到 TensorFlow JS:总结和常用平替整理


    demo展示

    这是一个剪刀石头布预测模型,会根据最近20局的历史数据训练模型,神经网络输入为最近2局的历史数据。

    如何拥有较为平滑的移植体验?

    一些碎碎念

    • JavaScript 不存在像 numpy 之于 python 一样著名且好用的数据处理库,所以请放弃对 JavaScript 原生类型 Array 进行操作的尝试,转而寻找基于 TensorFlow JS API 的解决方法。
    • JavaScript 作为一门前端语言,一大特色是包含了大量异步编程(即代码不是顺序执行的,浏览器自有一套标准去调整代码的执行顺序),这是为了保证前端页面不被卡死,所必备的性质。也因此,TensorFlow JS的函数中,许多输入输出传递的都不是数据,而是Promise对象。很多功能支持异步,但如果没有完全搞懂异步编程,不妨多用同步的思路:用 tf.Tensor.arraySync() 把 Tensor 的值取出,具体来说是将 Tensor 对象以同步的方式(即立即执行)拷贝生成出一个新的 array 对象。
    • Promise 对象是ES6新增的对象,一般与then一起使用,但掌握 async & await 就够了,这是更简洁的写法。
    • 多关注 API 文档中对象方法的返回类型,返回 Promise 对象则与异步编程相关,如果要获取Promise对象储存的值,需要在有 async function 包裹的代码中前置 await 关键字。
    • Pytorch 中的张量可以通过索引访问其元素,而 TensorFlow JS 则不能,需要转换为 array 进行访问。

    常用平替整理

    将张量转换为数组

    • Python, Pytorch:
    tensor = torch.tensor([1,2,3])
    np_array = tensor.numpy()
    • JS, tfjs:
    复制代码
    // 方式一:arraySync()
    let tensor = tf.tensor1d([1,2,3]);
    let array = tensor.arraySync();
    console.log(array); // [1,2,3]
    
    // 方式二:在async函数体内操作
    async function fun() {
        let tensor = tf.tensor1d([1,2,3]);
        let array = await tensor.array();
        console.log(array); // [1,2,3]
    }
    fun();
    
    // 注意,下面的写法是不行的,因为async函数的返回值是Promise对象
    array = async function (){
        return await tensor.array();
    }();
    console.log(array); // Promise object
    
    // 方式三:用then取出async函数返回Promise对象中的值
    let a
    (async function() {
        let array = await tensor.array(); 
        return array
    })().then(data => {a = data;})
    console.log(a); // [1,2,3]
    复制代码

    访问张量中的元素

    • Python,Pytorch:
    tensor = torch.tensor([1,2,3])
    print(tensor[0])
    print(tensor[-1])
    • JS,tfjs(不能直接通过访问tensor,需要转换成array):
    const tensor = tf.tensor1d([1,2,3]);
    const array = tensor.arraySync();
    console.log(array[0]);
    console.log(array[array.length - 1]);

    获取字典/对象的关键字

    • Python:
    actions = {'up':[1,0,0,0], 'down':[0,1,0,0], 'left':[0,0,1,0], 'right':[0,0,0,1]}
    actions_keys_list = list(actions.keys())
    • JS:
    const actions = {'up':[1,0,0,0], 'down':[0,1,0,0], 'left':[0,0,1,0], 'right':[0,0,0,1]};
    const actionsKeysArray = Object.keys(actions); 

    “先进先出”栈

    • Python:
    memory = [1,2,3]
    memory.append(4) # 入栈
    memory.pop(0) # 出栈
    • JS:
    let memory = [1,2,3];
    memory.push(4); // 入栈
    memory.splice(0,1); // 出栈

    “后进先出”栈

    • Python:
    memory = [1,2,3]
    memory.append(4) # 入栈
    memory.pop() # 出栈
    • JS:
    let memory = [1,2,3];
    memory.push(4); // 入栈
    memory.pop(); // 出栈

    根据概率分布采样元素

    • Python,Numpy:
    actions = ['up','down','left','right']
    prob = [0.1, 0.4, 0.4, 0.1]
    sample_action = np.random.choice(actions, p=prob))
    • JS,tfjs:
    const actions = ['up', 'down', 'left', 'right'];
    const prob = [0.1, 0.4, 0.4, 0.1];
    sampleActionIndex = tf.multinomial(prob, 1, null, true).arraySync(); // tf.Tensor 不能作为索引,需要用 arraySync() 同步地传输为 array
    sampleAction = actions[sampleActionIndex];

    找到数组中最大值的索引(Argmax)

    • Python,Numpy,Pyorch:
    复制代码
    actions = ['up', 'down', 'left', 'right']
    prob = [0.1, 0.3, 0.5, 0.1]
    prob_tensor = torch.tensor(prob)
    action_max_prob = actions[np.array(prob).argmax()] # np.array 可以作为索引
    action_max_prob = actions[prob_tensor.argmax().numpy()] # torch.tensor 不能作为索引,需要转换为 np.array 
    复制代码
    • JS, tfjs:
    const actions = ['up', 'down', 'left', 'right'];
    const prob = [0.1, 0.3, 0.5, 0.1];
    const probTensor = tf.tensor1d(prob); 
    const actionsMaxProb = actions[probTensor.argmax().arraySync()]; // tf.Tensor 不能作为索引,需要用 arraySync()同步地传输为 array

    生成等差数列数组

    • Python:
    range_list = list(range(1,10,1)) 
    • JS, tfjs:
    const rangeArray = tf.range(1, 10, 1).arraySync();

    打乱数组

    • Python:
    actions = ['up', 'down', 'left', 'right']
    print(random.shuffle(actions))
    • tfjs:(1)用 tf.util 类操作,处理常规的需求。
    const actions = ['up', 'down', 'left', 'right'];
    tf.util.shuffle(actions);
    console.log(actions);

     (2)用 tf.data.shuffle 操作,不建议,该类及其方法一般仅与 神经网络模型更新 绑定使用。

     极简逻辑回归

    • Python,Numpy,Pytorch:
    复制代码
    import numpy as np
    import torch
    from torch import nn
    import random
    
    
    class Memory(object):
        # 向Memory输送的数据可以是list,也可以是np.array
        def __init__(self, size=100, batch_size=32):
            self.memory_size = size
            self.batch_size = batch_size
            self.main = []
            
        def save(self, data):
            if len(self.main) == self.memory_size:
                self.main.pop(0)
            self.main.append(data)
    
        def sample(self):
            samples = random.sample(self.main, self.batch_size)
            return map(np.array, zip(*samples))
        
        
    class Model(object):
        # Model中所有方法的输入和返回都是np.array
        def __init__(self, lr=0.01, device=None):
            self.LR = lr
            self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # 调用GPU 若无则CPU
            self.network = nn.Sequential(nn.Flatten(), 
                                         nn.Linear(10, 32),
                                         nn.ReLU(),
                                         nn.Linear(32, 5),
                                         nn.Softmax(dim=1)).to(self.device)
            self.loss = nn.CrossEntropyLoss(reduction='mean')
            self.optimizer =  torch.optim.Adam(self.network.parameters(), lr=self.LR)
          
        def predict_nograd(self, _input):
            with torch.no_grad():
                _input = np.expand_dims(_input, axis=0)
                _input = torch.from_numpy(_input).float().to(self.device)
                _output = self.network(_input).cpu().numpy()
                _output = np.squeeze(_output)
            return _output
                
        def update(self, input_batch, target_batch):
            # 设置为训练模式
            self.network.train()
            _input_batch = torch.from_numpy(input_batch).float().to(self.device)
            _target_batch = torch.from_numpy(target_batch).float().to(self.device)
            
            self.optimizer.zero_grad()
            _evaluate_batch = self.network(_input_batch)
            batch_loss = self.loss(_evaluate_batch, _target_batch)
            batch_loss.backward()
            self.optimizer.step()
            batch_loss = batch_loss.item()
            
            # 设置为预测模式
            self.network.eval()
    
    
    if __name__ == '__main__':
        memory = Memory()
        model = Model()
        
        # 产生数据并输送到内存中
        # 假设一个5分类问题
        for i in range(memory.memory_size):
            example = np.random.randint(0,2,size=10)
            label = np.eye(5)[np.random.randint(0,5)]
            data = [example, label]
            memory.save(data)
        
        # 训练100次,每次从内存中随机抽取一个batch的数据
        for i in range(100):
            input_batch, target_batch = memory.sample()
            model.update(input_batch, target_batch)
        
        # 预测
        prediction = model.predict_nograd(np.random.randint(0,2,size=10))
        print(prediction)
    复制代码
    • JS,tfjs(网页应用一般不使用GPU):
    复制代码
    const Memory = {
        memorySize : 100,
        main : [],
    
        saveData : function (data) {
            // data = [input:array, label:array]
            if (this.main.length == this.memorySize) {
                this.main.splice(0,1);
            }
            this.main.push(data);
        },
    
        getMemoryTensor: function () {
            let inputArray = [],
            labelArray = [];
            for (let i = 0; i < this.main.length; i++) {
                inputArray.push(this.main[i][0])
                labelArray.push(this.main[i][1])
            }
            return {
                inputBatch: tf.tensor2d(inputArray),
                labelBatch: tf.tensor2d(labelArray)
            }
        }
    }
    
    const Model = {
        batchSize: 32,
        epoch: 200,
        network: tf.sequential({
            layers: [
                tf.layers.dense({inputShape: [10], units: 16, activation: 'relu'}),
                tf.layers.dense({units: 5, activation: 'softmax'}),
            ]
        }),
        
        compile: function () {
            this.network.compile({
                optimizer: tf.train.sgd(0.1),
                shuffle: true,
                loss: 'categoricalCrossentropy',
                metrics: ['accuracy']
            });
        },
    
        predict: function (input) {
            // input = array
            // Return tensor1d
            return this.network.predict(tf.tensor2d([input])).squeeze();
        },
    
        update: async function (inputBatch, labelBatch) {
            // inputBatch = tf.tensor2d(memorySize × 10)
            // labelBatch = tf.tensor2d(memorySize × 5)
            this.compile();
    
            await this.network.fit(inputBatch, labelBatch, {
                epochs: this.epoch,
                batchSize: this.batchSize
            }).then(info => {
                console.log('Final accuracy', info.history.acc);
            });
        }
    }
    
    // 假设一个5分类问题
    // 随机生成样例和标签,并填满内存
    let example, label, rnd, data;
    for (let i = 0; i < Memory.memorySize; i++) {
        example = tf.multinomial(tf.tensor1d([.5, .5]), 10).arraySync();
        rnd = Math.floor(Math.random()*5);
        label = tf.oneHot(tf.tensor1d([rnd], 'int32'), 5).squeeze().arraySync();
        data = [example, label];
        Memory.saveData(data);
    }
    
    // 将内存中储存的数据导出为tensor,并训练模型
    let {inputBatch, labelBatch} = Memory.getMemoryTensor();
    Model.update(inputBatch, labelBatch);
    复制代码

     

     

     

     

     

     

     

     
  • 相关阅读:
    Windows终端配置emoji
    健康防猝指南2:饮食健康
    神奇的卡尔曼滤波,行人追踪的福音
    不直接修改node_modules中包的解决办法
    使用 MongoDB 构建 AI:Flagler Health 的 AI 旅程如何彻底改变患者护理
    数据结构——堆排序(C语言)
    【006期】用Processing写一个根据音乐变化的黑白格游戏
    SpringBoot JavaBean对象拷贝 orika
    在Ubuntu20.04安装StarRocks On Docker并在DataGrip配置JDBC协议连接容器内StarRocks2.3.2
    docker-compose教程(安装,使用, 快速入门)
  • 原文地址:https://www.cnblogs.com/wsy950409/p/16421861.html