//=========================================================================== /*! * * * \brief A kernel expansion with support of missing features * * * * \author B. Li * \date 2012 * * * \par Copyright 1995-2017 Shark Development Team * *

* This file is part of Shark. * * * Shark is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Shark is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Shark. If not, see . * */ //=========================================================================== #include #include #include #include namespace shark { /// Kernel expansion with missing features support template class MissingFeaturesKernelExpansion : public KernelExpansion { private: typedef KernelExpansion Base; public: typedef typename Base::KernelType KernelType; typedef typename Base::BatchInputType BatchInputType; typedef typename Base::BatchOutputType BatchOutputType; /// Constructors from the base class ///@{ MissingFeaturesKernelExpansion(){} MissingFeaturesKernelExpansion(KernelType* kernel) : Base(kernel) {} MissingFeaturesKernelExpansion(KernelType* kernel, Data const& basis, bool offset) : Base(kernel, basis, offset, 1u) {} ///@} /// \brief From INameable: return the class name. std::string name() const { return "MissingFeaturesKernelExpansion"; } boost::shared_ptr createState()const{ return boost::shared_ptr(new EmptyState()); } /// Override eval(...) in the base class virtual void eval(BatchInputType const& patterns, BatchOutputType& outputs)const{ SHARK_ASSERT(Base::mep_kernel); SIZE_CHECK(Base::m_alpha.size1() > 0u); //Todo: i am too lazy to us iterated loops in this function. //so i am using a DataView to have O(1) random access lookup. but this is not needed! DataView const > indexedBasis(Base::m_basis); ensure_size(outputs,batchSize(patterns),Base::outputShape().numElements()); if (Base::hasOffset()) noalias(outputs) = repeat(Base::m_b,batchSize(patterns)); else outputs.clear(); for(std::size_t p = 0; p != batchSize(patterns); ++p){ // Calculate scaling coefficient for the 'pattern' const double patternNorm = computeNorm(column(Base::m_alpha, 0), m_scalingCoefficients, row(patterns,p)); const double patternSc = patternNorm / m_classifierNorm; // Do normal classification except that we use kernel which supports inputs with Missing features //TODO: evaluate k for all i and replace the += with a matrix-vector operation. //better: do this for all p and i and go matrix-matrix-multiplication for (std::size_t i = 0; i != indexedBasis.size(); ++i){ const double k = evalSkipMissingFeatures( *Base::mep_kernel, indexedBasis[i], row(patterns,p)) / m_scalingCoefficients[i] / patternSc; noalias(row(outputs,p)) += k * row(Base::m_alpha, i); } } } void eval(BatchInputType const& patterns, BatchOutputType& outputs, State & state)const{ eval(patterns, outputs); } /// Calculate norm of classifier, i.e., ||w|| /// /// formula: /// \f$ \sum_{i,j=1}^{n}\alpha_i\frac{y_i}{s_i}K\left(x_i,x_j)\right)\frac{y_j}{s_j}\alpha_j \f$ /// where \f$ s_i \f$ is scaling coefficient, and \f$ K \f$ is kernel function, /// \f$ K\left(x_i,x_j)\right) \f$ is taken only over features that are valid for both \f$ x_i \f$ and \f$ x_j \f$ template double computeNorm( const RealVector& alpha, const RealVector& scalingCoefficient, InputTypeT const& missingness ) const{ SHARK_ASSERT(Base::mep_kernel); SIZE_CHECK(alpha.size() == scalingCoefficient.size()); SIZE_CHECK(Base::m_basis.numberOfElements() == alpha.size()); // Calculate ||w||^2 double norm_sqr = 0.0; //Todo: i am too lazy to use iterated loops in this function. //so i am using a DataView to have O(1) random access lookup. but this is not needed! DataView const > indexedBasis(Base::m_basis); for (std::size_t i = 0; i < alpha.size(); ++i){ for (std::size_t j = 0; j < alpha.size(); ++j){ const double evalResult = evalSkipMissingFeatures( *Base::mep_kernel, indexedBasis[i], indexedBasis[j], missingness); // Note that in Shark solver, we do axis flip by substituting \alpha with y \times \alpha norm_sqr += evalResult * alpha(i) * alpha(j) / scalingCoefficient(i) / scalingCoefficient(j); } } // Return ||w|| return std::sqrt(norm_sqr); } double computeNorm( const RealVector& alpha, const RealVector& scalingCoefficient ) const{ SHARK_ASSERT(Base::mep_kernel); SIZE_CHECK(alpha.size() == scalingCoefficient.size()); SIZE_CHECK(Base::m_basis.numberOfElements() == alpha.size()); //Todo: i am too lazy to us iterated loops in this function. //so i am using a DataView to have O(1) random access lookup. but this is not needed! DataView const > indexedBasis(Base::m_basis); // Calculate ||w||^2 double norm_sqr = 0.0; for (std::size_t i = 0; i < alpha.size(); ++i){ for (std::size_t j = 0; j < alpha.size(); ++j){ const double evalResult = evalSkipMissingFeatures( *Base::mep_kernel, indexedBasis[i], indexedBasis[j]); // Note that in Shark solver, we do axis flip by substituting \alpha with y \times \alpha norm_sqr += evalResult * alpha(i) * alpha(j) / scalingCoefficient(i) / scalingCoefficient(j); } } // Return ||w|| return std::sqrt(norm_sqr); } void setScalingCoefficients(const RealVector& scalingCoefficients) { #if DEBUG for(double v: scalingCoefficients) { SHARK_ASSERT(v > 0.0); } #endif m_scalingCoefficients = scalingCoefficients; } void setClassifierNorm(double classifierNorm) { SHARK_ASSERT(classifierNorm > 0.0); m_classifierNorm = classifierNorm; } protected: /// The scaling coefficients RealVector m_scalingCoefficients; /// The norm of classifier(w) double m_classifierNorm; }; } // namespace shark {