//===========================================================================
/*!
*
*
* \brief Affine linear kernel function expansion
*
* \par
* Affine linear kernel expansions resulting from Support
* vector machine (SVM) training and other kernel methods.
*
*
*
*
* \author T. Glasmachers
* \date 2007-2011
*
*
* \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 .
*
*/
//===========================================================================
#ifndef SHARK_MODELS_KERNELEXPANSION_H
#define SHARK_MODELS_KERNELEXPANSION_H
#include
#include
#include
#include
namespace shark {
///
/// \brief Linear model in a kernel feature space.
///
/// An affine linear kernel expansion is a model of the type
/// \f[ x : \mathbb{R}^d \to \mathbb{R}^d \enspace , \enspace x \mapsto \sum_{n=1}^{\ell} \alpha_n k(x_n, x) + b \enspace ,\f]
/// with parameters \f$ \alpha_n \in \mathbb{R}^{d} \f$ for all
/// \f$ n \in \{1, \dots, \ell\} \f$ and \f$ b \in \mathbb{R}^d \f$.
///
/// One way in which the possibility for vector-valued input and output of dimension \f$ d \f$ may be interpreted
/// is as allowing for a KernelExpansion model for \f$ d \f$ different classes/outputs in multi-class problems. Then,
/// the i-th column of the matrix #m_alpha is the KernelExpansion for class/output i.
///
/// \tparam InputType Type of basis elements supplied to the kernel
///
template
class KernelExpansion : public AbstractModel
{
public:
typedef AbstractKernelFunction KernelType;
typedef AbstractModel base_type;
typedef typename base_type::BatchInputType BatchInputType;
typedef typename base_type::BatchOutputType BatchOutputType;
// //////////////////////////////////////////////////////////
// //////////// CONSTRUCTORS /////////////////////
// //////////////////////////////////////////////////////////
KernelExpansion():mep_kernel(NULL){}
KernelExpansion(KernelType* kernel):mep_kernel(kernel){
SHARK_ASSERT(kernel != NULL);
}
KernelExpansion(KernelType* kernel, Data const& basis,bool offset, std::size_t outputs = 1){
SHARK_ASSERT(kernel != NULL);
setStructure(kernel, basis,offset,outputs);
}
void setStructure(KernelType* kernel, Data const& basis,bool offset, std::size_t outputs = 1){
SHARK_ASSERT(kernel != NULL);
mep_kernel = kernel;
if(offset)
m_b.resize(outputs);
m_basis = basis;
m_alpha.resize(basis.numberOfElements(), outputs);
m_alpha.clear();
}
/// \brief From INameable: return the class name.
std::string name() const
{ return "KernelExpansion"; }
/// dimensionality of the output RealVector
Shape outputShape() const{
return m_alpha.size2();
}
Shape inputShape() const{
return Shape();
}
// //////////////////////////////////////////////////////////
// /////////// ALL THINGS KERNEL //////////////////////
// //////////////////////////////////////////////////////////
KernelType const* kernel() const{
return mep_kernel;
}
KernelType* kernel(){
return mep_kernel;
}
void setKernel(KernelType* kernel){
mep_kernel = kernel;
}
// //////////////////////////////////////////////////////////
// /////// ALL THINGS ALPHA AND OFFSET ////////////////
// //////////////////////////////////////////////////////////
bool hasOffset() const{
return m_b.size() != 0;
}
RealMatrix& alpha(){
return m_alpha;
}
RealMatrix const& alpha() const{
return m_alpha;
}
double& alpha(std::size_t example, std::size_t cls){
return m_alpha(example, cls);
}
double const& alpha(std::size_t example, std::size_t cls) const{
return m_alpha(example, cls);
}
RealVector& offset(){
SHARK_RUNTIME_CHECK(hasOffset(), "[KernelExpansion::offset] invalid call for object without offset term");
return m_b;
}
RealVector const& offset() const{
SHARK_RUNTIME_CHECK(hasOffset(), "[KernelExpansion::offset] invalid call for object without offset term");
return m_b;
}
double& offset(std::size_t cls){
SHARK_RUNTIME_CHECK(hasOffset(), "[KernelExpansion::offset] invalid call for object without offset term");
return m_b(cls);
}
double const& offset(std::size_t cls) const{
SHARK_RUNTIME_CHECK(hasOffset(), "[KernelExpansion::offset] invalid call for object without offset term");
return m_b(cls);
}
// //////////////////////////////////////////////////////////
// //////// ALL THINGS UNDERLYING DATA ////////////////
// //////////////////////////////////////////////////////////
Data const& basis() const {
return m_basis;
}
Data& basis() {
return m_basis;
}
/// The sparsify method removes non-support-vectors from
/// its set of basis vectors and the coefficient matrix.
void sparsify(){
std::size_t ic = m_basis.numberOfElements();
std::vector svIndices;
for (std::size_t i=0; i != ic; ++i){
if (blas::norm_1(row(m_alpha, i)) > 0.0){
svIndices.push_back(i);
}
}
//project basis on the support vectors
m_basis = toDataset(subset(toView(m_basis),svIndices));
//reduce alpha to it's support vector variables
RealMatrix a(svIndices.size(), m_alpha.size2());
for (std::size_t i=0; i!= svIndices.size(); ++i){
noalias(row(a,i)) = row(m_alpha,svIndices[i]);
}
swap(m_alpha,a);
}
// //////////////////////////////////////////////////////////
// //////// ALL THINGS KERNEL PARAMETERS //////////////
// //////////////////////////////////////////////////////////
RealVector parameterVector() const{
if (hasOffset()){
return to_vector(m_alpha) | m_b;
}
else{
return to_vector(m_alpha);
}
}
void setParameterVector(RealVector const& newParameters){
SHARK_RUNTIME_CHECK(newParameters.size() == numberOfParameters(), "Invalid size of the parameter vector");
std::size_t numParams = m_alpha.size1() * m_alpha.size2();
noalias(to_vector(m_alpha)) = subrange(newParameters, 0, numParams);
if (hasOffset())
noalias(m_b) = subrange(newParameters, numParams, numParams + m_b.size());
}
std::size_t numberOfParameters() const{
if (hasOffset())
return m_alpha.size1() * m_alpha.size2() + m_b.size();
else
return m_alpha.size1() * m_alpha.size2();
}
// //////////////////////////////////////////////////////////
// //////// ALL THINGS EVALUATION //////////////
// //////////////////////////////////////////////////////////
boost::shared_ptr createState()const{
return boost::shared_ptr(new EmptyState());
}
using AbstractModel::eval;
void eval(BatchInputType const& patterns, BatchOutputType& output)const{
std::size_t numPatterns = batchSize(patterns);
SHARK_ASSERT(mep_kernel != NULL);
output.resize(numPatterns,m_alpha.size2());
if (hasOffset())
output = repeat(m_b,numPatterns);
else
output.clear();
std::size_t batchStart = 0;
for (std::size_t i=0; i != m_basis.numberOfBatches(); i++){
std::size_t batchEnd = batchStart+batchSize(m_basis.batch(i));
//evaluate kernels
//results in a matrix of the form where a column consists of the kernel evaluation of
//pattern i with respect to the batch of the basis,this gives a good memory alignment
//in the following matrix matrix product
RealMatrix kernelEvaluations = (*mep_kernel)(m_basis.batch(i),patterns);
//get the part of the alpha matrix which is suitable for this batch
auto batchAlpha = subrange(m_alpha,batchStart,batchEnd,0,m_alpha.size2());
noalias(output) += prod(trans(kernelEvaluations),batchAlpha);
batchStart = batchEnd;
}
}
void eval(BatchInputType const& patterns, BatchOutputType& outputs, State & state)const{
eval(patterns, outputs);
}
// //////////////////////////////////////////////////////////
// //////// ALL THINGS SERIALIZATION //////////////
// //////////////////////////////////////////////////////////
/// From ISerializable, reads a model from an archive
void read( InArchive & archive ){
SHARK_ASSERT(mep_kernel != NULL);
archive >> m_alpha;
archive >> m_b;
archive >> m_basis;
archive >> (*mep_kernel);
}
/// From ISerializable, writes a model to an archive
void write( OutArchive & archive ) const{
SHARK_ASSERT(mep_kernel != NULL);
archive << m_alpha;
archive << m_b;
archive << m_basis;
archive << const_cast(*mep_kernel);//prevent compilation warning
}
// //////////////////////////////////////////////////////////
// //////// MEMBERS //////////////
// //////////////////////////////////////////////////////////
protected:
/// kernel function used in the expansion
KernelType* mep_kernel;
/// "support" basis vectors
Data m_basis;
/// kernel coefficients
RealMatrix m_alpha;
/// offset or bias term
RealVector m_b;
};
///
/// \brief Linear classifier in a kernel feature space.
///
/// This model is a simple wrapper for the KernelExpansion calculating the arg max
/// of the outputs of the model. This is the model used by kernel classifier models like SVMs.
///
template
struct KernelClassifier: public Classifier >{
typedef AbstractKernelFunction KernelType;
typedef KernelExpansion KernelExpansionType;
KernelClassifier()
{ }
KernelClassifier(KernelType* kernel)
: Classifier >(KernelExpansionType(kernel))
{ }
KernelClassifier(KernelExpansionType const& decisionFunction)
: Classifier >(decisionFunction)
{ }
std::string name() const
{ return "KernelClassifier"; }
};
}
#endif