Shark源码分析三:数据预处理之正则化

Shark源码分析(三):数据预处理之正则化

在机器学习算法中,获取训练数据后首先要做的不是将输入投入训练方法中进行学习,而是应该对数据进行预处理。预处理过程输出数据的质量能够对之后算法的结果起着至关重要的作用。预处理过程含有非常多的操作,在我目前阅读代码的过程中只碰到了正则化这一过程。那我们就先来讨论正则化,如果之后再碰到了其他的方法再补充。

Shark将对输入数据进行正则化的模型也看作是一个线性模型。Shark给出了两种正则化的方法,分别是NormalizeComponentsUnitInterval,NormalizeComponentsUnitVariance。对于这两种不同的正则化方法有两个不同的trainer(联系上一篇博客的内容)进行训练。

第一种方法是将每一维度的特征都缩小到 [0,1] 范围内,这对于特征值是有界的情况来说是有效的。而第二种方法是将每一维度的方差都调整到1。对于『将均值变为0』这一操作来说是可选的。如果不包含这一操作,那么这一方法对于高维度的稀疏特征向量来说是有效的。

Normalizer类

该类定义在<include/shark/Models/Normalizer.h>文件中。

其与普通线性模型的不同之处在于:

  • 输入与输出的维度必须是相同的
  • 对于每一维度需要单独进行计算
template <class DataType = RealVector>
class Normalizer : public AbstractModel<DataType,DataType>
{
protected:
    RealVector m_A; //权值向量 
    RealVector m_b; //偏置向量 bool m_hasOffset; //表示是否需要偏置向量 
public:
    typedef AbstractModel<DataType,DataType> base_type;
    typedef Normalizer<DataType> self_type;

    typedef typename base_type::BatchInputType BatchInputType;
    typedef typename base_type::BatchOutputType BatchOutputType;

    Normalizer()
    { }

    Normalizer(const self_type& model)
    : m_A(model.m_A),m_b(model.m_b),m_hasOffset(model.m_hasOffset)
    { }

    Normalizer(std::size_t dimension,bool hasOffset = false)
    : m_A(dimension,dimension),m_b(dimension),m_hasOffset(hasOffset)
    { }

    Normalizer(RealVector diagonal)
    : m_A(diagonal),m_hasOffset(false)
    { }

    Normalizer(RealVector diagonal,RealVector vector)
    : m_A(diagonal),m_b(vector),m_hasOffset(true)
    { }

    std::string name() const
    { return "Normalizer"; }

    friend void swap(const Normalizer& model1,const Normalizer& model2)
    {
        std::swap(model1.m_A,model2.m_A);
        std::swap(model1.m_b,model2.m_b);
        std::swap(model1.m_hasOffset,model2.m_hasOffset);
    }

    const self_type operator = (const self_type& model)
    {
        m_A = model.m_A;
        m_b = model.m_b;
        m_hasOffset = model.m_hasOffset;
    }

    boost::shared_ptr<State> createState() const
    {
        return boost::shared_ptr<State>(new EmptyState());
    }

    //判断模型是否有被正确地初始化
    bool isValid() const
    {
        return (m_A.size() != 0);
    }

    bool hasOffset() const
    {
        return m_hasOffset;
    }

    //返回权值向量
    RealVector const& diagonal() const
    {
        SHARK_CHECK(isValid(),"[Normalizer::matrix] model is not initialized");
        return m_A;
    }

    //返回偏置向量
    RealVector const& offset() const
    {
        SHARK_CHECK(isValid(),"[Normalizer::vector] model is not initialized");
        return m_b;
    }

    std::size_t inputSize() const
    {
        SHARK_CHECK(isValid(),"[Normalizer::inputSize] model is not initialized");
        return m_A.size();
    }

    std::size_t outputSize() const
    {
        SHARK_CHECK(isValid(),"[Normalizer::outputSize] model is not initialized");
        return m_A.size();
    }

    //将权值向量与偏置向量合在一起进行输出
    RealVector parameterVector() const
    {
        SHARK_CHECK(isValid(),"[Normalizer::parameterVector] model is not initialized");
        std::size_t dim = m_A.size();
        if (hasOffset())
        {
            RealVector param(2 * dim);
            init(param)<<m_A,m_b;
            return param;
        }
        else
        {
            RealVector param(dim);
            init(param)<<m_A;
            return param;
        }
    }

    //可以更改权值向量与偏置向量的值
    void setParameterVector(RealVector const& newParameters)
    {
        SHARK_CHECK(isValid(),"[Normalizer::setParameterVector] model is not initialized");
        std::size_t dim = m_A.size();
        if (hasOffset())
        {
            SIZE_CHECK(newParameters.size() == 2 * dim);
            init(newParameters)>>m_A,m_b;
        }
        else
        {
            SIZE_CHECK(newParameters.size() == dim);
            init(newParameters)>>m_A;
        }
    }

    std::size_t numberOfParameters() const
    {
        SHARK_CHECK(isValid(),"[Normalizer::numberOfParameters] model is not initialized");
        return (m_hasOffset) ? m_A.size() + m_b.size() : m_A.size();
    }

    //在训练完成之后,将权值向量与偏置向量保存到模型中
    void setStructure(RealVector const& diagonal)
    {
        m_A = diagonal;
        m_hasOffset = false;
    }

    void setStructure(std::size_t dimension,bool hasOffset = false)
    {
        m_A.resize(dimension);
        m_hasOffset = hasOffset;
        if (hasOffset) m_b.resize(dimension);
    }

    void setStructure(RealVector const& diagonal,RealVector const& offset)
    {
        SHARK_CHECK(diagonal.size() == offset.size(),"[Normalizer::setStructure] dimension conflict");
        m_A = diagonal;
        m_b = offset;
        m_hasOffset = true;
    }

    using base_type::eval;

    //对输入利用权值向量以及偏置向量进行正则化
    void eval(BatchInputType const& input,BatchOutputType& output) const
    {
        SHARK_CHECK(isValid(),"[Normalizer::eval] model is not initialized");
        output.resize(input.size1(),input.size2());
        noalias(output) = input * repeat(m_A,input.size1());
        if (hasOffset())
        {
            noalias(output) += repeat(m_b,input.size1());
        }
    }

    void eval(BatchInputType const& input,BatchOutputType& output,State& state) const
    {
        eval(input,output);
    }

    void read(InArchive& archive)
    {
        archive & m_A;
        archive & m_b;
        archive & m_hasOffset;
    }

    void write(OutArchive& archive) const
    {
        archive & m_A;
        archive & m_b;
        archive & m_hasOffset;
    }
};

注意到这个类也是继承自AbstractModel。

正则化模型的使用方式是,利用输入的训练数据训练正则化模型的参数。如果之后有测试数据输入进来,可以使用同样的模型对测试数据进行正则化,而不需要再重新训练模型。

NormalizeComponentsUnitVariance类

该类定义在<include/shark/Algorithms/Trainers/NormalizeComponentsUnitVariance.h>文件中。

在这个方法中,零均值化默认是被关闭的。因为对输入是一个稀疏矩阵的情况来说,零均值话是会破坏它的稀疏性。如果当输入数据不是稀疏的话,可以开启这一项。

其正则化方法是 x=xμσ=1σxμσ

template <class DataType = RealVector>
class NormalizeComponentsUnitVariance : public AbstractUnsupervisedTrainer< Normalizer<DataType> >
{
public:
    typedef AbstractUnsupervisedTrainer< Normalizer<DataType> > base_type;

    NormalizeComponentsUnitVariance(bool zeroMean)
    : m_zeroMean(zeroMean){ }

    std::string name() const
    { return "NormalizeComponentsUnitVariance"; }

    void train(Normalizer<DataType>& model,UnlabeledData<DataType> const& input)
    {
        SHARK_CHECK(input.numberOfElements() >= 2,"[NormalizeComponentsUnitVariance::train] input needs to consist of at least two points");
        std::size_t dc = dataDimension(input);

        RealVector mean;
        RealVector variance;
        meanvar(input,mean,variance); //计算数据的均值,方差

        RealVector diagonal(dc);
        RealVector vector(dc);

        for (std::size_t d=0; d != dc; d++){
            double stddev = std::sqrt(variance(d));
            if (stddev == 0.0)
            {
                diagonal(d) = 0.0;
                vector(d) = 0.0;
            }
            else
            {
                diagonal(d) = 1.0 / stddev;
                vector(d) = -mean(d) / stddev;
            }
        }

        if (m_zeroMean) 
            model.setStructure(diagonal,vector);
        else 
            model.setStructure(diagonal);
    }

protected:
    bool m_zeroMean;
};

注意到该方法是继承自AbstractUnsupervisedTrainer,也把它作为无监督学习方法。

NormalizeComponentsUnitInterval类

该类定义在<include/shark/Algorithms/Trainers/NormalizeComponentsUnitInterval.h>文件中。

该方法是将数据的范围都变换到 [0,1] 区间内,这样做的话势必会破坏数据的稀疏性,所以可能会偏向于选择NormalizeComponentsUnitVariance这一方法。

该方法的公式是 x=xminmaxmin

template <class DataType = RealVector>
class NormalizeComponentsUnitInterval : public AbstractUnsupervisedTrainer< Normalizer<DataType> >
{
public:
    typedef AbstractUnsupervisedTrainer< Normalizer<DataType> > base_type;

    NormalizeComponentsUnitInterval()
    { }

    std::string name() const
    { return "NormalizeComponentsUnitInterval"; }

    void train(Normalizer<DataType>& model,UnlabeledData<DataType> const& input)
    {
        std:: size_t ic = input.numberOfElements();
        SHARK_CHECK(ic >= 2,"[NormalizeComponentsUnitInterval::train] input needs to consist of at least two points");
        std::size_t dc = dataDimension(input);

       //取出每一维度的第一个数据
       //求出每一维度的最大值和最小值
        RealVector min = input.element(0);
        RealVector max = input.element(0);
        for(std::size_t i=1; i != ic; i++){
            for(std::size_t d = 0; d != dc; d++){
                double x = input.element(i)(d);
                min(d) = std::min(min(d),x);
                max(d) = std::max(max(d),x);
            }
        }

        RealVector diagonal(dc);
        RealVector offset(dc);

        for (std::size_t d=0; d != dc; d++)
        {
            if (min(d) == max(d)) //这一维数据的值相同
            {
                diagonal(d) = 0.0;
                offset(d) = -min(d) + 0.5;//偏移设置成0.5就好
            }
            else
            {
                double n = 1.0 / (max(d) - min(d));
                diagonal(d) = n;
                offset(d) = -min(d) * n;
            }
        }

        model.setStructure(diagonal,offset);
    }
};

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


jquery.validate使用攻略(表单校验) 目录 jquery.validate使用攻略1 第一章&#160;jquery.validate使用攻略1 第二章&#160;jQuery.validate.js API7 Custom selectors7 Utilities8 Validato
/\s+/g和/\s/g的区别 正则表达式/\s+/g和/\s/g,目的均是找出目标字符串中的所有空白字符,但两者到底有什么区别呢? 我们先来看下面一个例子: let name = &#39;ye wen jun&#39;;let ans = name.replace(/\s/g, &#39;&#3
自整理几个jquery.Validate验证正则: 1. 只能输入数字和字母 /^[0-9a-zA-Z]*$/g jQuery.validator.addMethod(&quot;letters&quot;, function (value, element) { return this.optio
this.optional(element)的用法 this.optional(element)是jquery.validator.js表单验证框架中的一个函数,用于表单控件的值不为空时才触发验证。 简单来说,就是当表单控件值为空的时候不会进行表单校验,此函数会返回true,表示校验通过,当表单控件
jQuery.validate 表单动态验证 实际上jQuery.validate提供了动态校验的方法。而动态拼JSON串的方式是不支持动态校验的。牺牲jQuery.validate的性能优化可以实现(jQuery.validate的性能优化见图1.2 jQuery.validate源码 )。 也可
自定义验证之这能输入数字(包括小数 负数 ) &lt;script type=&quot;text/javascript&quot;&gt; function onlyNumber(obj){ //得到第一个字符是否为负号 var t = obj.value.charAt(0); //先把非数字的都
// 引入了外部的验证规则 import { validateAccountNumber } from &quot;@/utils/validate&quot;; validator.js /*是否合法IP地址*/ export function validateIP(rule, value,cal
VUE开发--表单验证(六十三) 一、常用验证方式 vue 中表单字段验证的写法和方式有多种,常用的验证方式有3种: data 中验证 表单内容: &lt;!-- 表单 --&gt; &lt;el-form ref=&quot;rulesForm&quot; :rules=&quot;formRul
正则表达式 座机的: 例子: 座机有效写法: 0316-8418331 (010)-67433539 (010)67433539 010-67433539 (0316)-8418331 (0316)8418331 正则表达式写法 0\d{2,3}-\d{7,8}|\(?0\d{2,3}[)-]?\d
var reg = /^0\.[1-9]{0,2}$/;var linka = 0.1;console.log (reg.test (linka)); 0到1两位小数正则 ^(0\.(0[1-9]|[1-9]{1,2}|[1-9]0)$)|^1$ 不含0、0.0、0.00 // 验证是否是[1-10
input最大长度限制问题 &lt;input type=&quot;text&quot; maxlength=&quot;5&quot; /&gt; //可以 &lt;input type=&quot;number&quot; maxlength=&quot;5&quot; /&gt; //没有效
js输入验证是否为空、是否为null、是否都是空格 目录 1.截头去尾 trim 2.截头去尾 会去掉开始和结束的空格,类似于trim 3.会去掉所有的空格,包括开始,结束,中间 1.截头去尾 trim str=str.trim(); // 强烈推荐 最常用、最实用 or $.trim(str);
正则表达式语法大全 字符串.match(正则):返回符合的字符串,若不满足返回null 字符串.search(正则):返回搜索到的位置,若非一个字符,则返回第一个字母的下标,若不匹配则返回-1 字符串.replace(正则,新的字符串):找到符合正则的内容并替换 正则.test(字符串):在字符串中
正整数正则表达式正数的正则表达式(包括0,小数保留两位): ^((0{1}.\d{1,2})|([1-9]\d.{1}\d{1,2})|([1-9]+\d)|0)$正数的正则表达式(不包括0,小数保留两位): ^((0{1}.\d{1,2})|([1-9]\d.{1}\d{1,2})|([1-9]+
JS 正则验证 test() /*用途:检查输入手机号码是否正确输入:s:字符串返回:如果通过验证返回true,否则返回false /function checkMobile(s){var regu =/[1][3][0-9]{9}$/;var re = new RegExp(regu);if (r
请输入保留两位小数的销售价的正则: /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/ 1.只能输入英文 &lt;input type=&quot;text&quot; onkeyup=&quot;value
判断价格的正则表达式 价格的正则表达式 /(^[1-9]\d*(\.\d{1,2})?$)|(^0(\.\d{1,2})?$)/; 1 解析:价格符合两种格式 ^ [1-9]\d*(.\d{1,2})?$ : 1-9 开头,后跟是 0-9,可以跟小数点,但小数点后要带上 1-2 位小数,类似 2,2
文章浏览阅读106次。这篇文章主要介绍了最实用的正则表达式整理,比如校验邮箱的正则,号码相关,数字相关等等,本文给大家列举的比较多,需要的朋友可以参考下。_/^(?:[1-9]d*)$/ 手机号
文章浏览阅读1.2k次。4、匹配中的==、an==、== an9、i9 == "9i"和99p==请注意下面这部分的作用,它在匹配中间内容的时候排除了说明:当html字符串如下时,可以匹配到两处,表示匹配的字符串不包含and且不包含空白字符。说明:在上面的正则表达式中,_gvim正则表达式匹配不包含某个字符串
文章浏览阅读897次。【代码】正则表达式匹配a标签的href。_auto.js 正则匹配herf