2014-10-22 2 views
1

Я пробовал прилагаемый код для linear regression with automatic differentiation. Указывает тип данных [Двойной] [2], выполненный из двух плавающих элементов, и объявляет его экземпляром Num, Fractional и Floating. Как и во всех задачах фитинга/регрессии, существует функция скалярной стоимости, параметризованная параметрами подгонки c и m, и оптимизатор, который улучшает оценку этих двух параметров с помощью градиентного спуска.«Нет экземпляра для ..», код Haskell'98 на GHC7.8.3

Вопрос Я использую GHC 7.8.3, и авторы явно указать, что это H98 код (я упомянул об этом в названии, потому что это единственное существенное различие, которое я могу думать между моей установкой и автором , однако plz правильно, если неправильно). Почему он задыхается в определении функции стоимости? Мое понимание: функции idD и constD map Floats to Duals, g является полиморфным (он может выполнять алгебраические операции с двумя входами, потому что Dual наследуется от Num, Fractional и Floating) и производные карты Duals to Doubles. Подразумевается сигнатура типа для g (функция сокращения стоимости по этане по данным). Я попытался опустить его и сделать его более общим, заменив ограничение Floating на Fractional. Кроме того, я попытался преобразовать числовые типы c и m inline с (fromIntegral c :: Double), но безрезультатно.

Конкретно этот код дает эту ошибку:

No instance for (Integral Dual) arising from a use of ‘g’ 
In the first argument of ‘flip’, namely ‘g’ 
In the expression: flip g (constD c) 
In the second argument of ‘($)’, namely ‘flip g (constD c) $ idD m’ 

Любые намеки, пожалуйста? Я уверен, что это очень пустой вопрос, но я просто не понимаю.

Полный код выглядит следующим образом:

{-# LANGUAGE NoMonomorphismRestriction #-} 

module ADfw (Dual(..), f, idD, cost) where 

data Dual = Dual Double Double deriving (Eq, Show) 

constD :: Double -> Dual 
constD x = Dual x 0 

idD :: Double -> Dual 
idD x = Dual x 1.0 

instance Num Dual where 
    fromInteger n    = constD $ fromInteger n 
    (Dual x x') + (Dual y y') = Dual (x+y) (x' + y') 
    (Dual x x') * (Dual y y') = Dual (x*y) (x*y' + y*x') 
    negate (Dual x x')  = Dual (negate x) (negate x') 
    signum _     = undefined 
    abs _      = undefined 

instance Fractional Dual where 
    fromRational p = constD $ fromRational p 
    recip (Dual x x') = Dual (1.0/x) (- x'/(x*x)) 

instance Floating Dual where 
    pi = constD pi 
    exp (Dual x x') = Dual (exp x) (x' * exp x) 
    log (Dual x x') = Dual (log x) (x'/x) 
    sqrt (Dual x x') = Dual (sqrt x) (x'/(2 * sqrt x)) 
    sin (Dual x x') = Dual (sin x) (x' * cos x) 
    cos (Dual x x') = Dual (cos x) (x' * (- sin x)) 
    sinh (Dual x x') = Dual (sinh x) (x' * cosh x) 
    cosh (Dual x x') = Dual (cosh x) (x' * sinh x) 
    asin (Dual x x') = Dual (asin x) (x'/sqrt (1 - x*x)) 
    acos (Dual x x') = Dual (acos x) (x'/(-sqrt (1 - x*x))) 
    atan (Dual x x') = Dual (atan x) (x'/(1 + x*x)) 
    asinh (Dual x x') = Dual (asinh x) (x'/sqrt (1 + x*x)) 
    acosh (Dual x x') = Dual (acosh x) (x'/(sqrt (x*x - 1))) 
    atanh (Dual x x') = Dual (atanh x) (x'/(1 - x*x)) 

-- example 
-- f = sqrt . (* 3) . sin 
-- f' x = 3 * cos x/(2 * sqrt (3 * sin x)) 

-- linear fit sum-of-squares cost 
-- cost :: Fractional s => s -> s -> [s] -> [s] -> s 
cost m c x y = (/ (2 * (fromIntegral $ length x))) $ 
       sum $ zipWith errSq x y 
    where 
    errSq xi yi = zi * zi 
     where 
     zi = yi - (m * xi + c) 

-- test data 
x_ = [1..10] 
y_ = [a | a <- [1..20], a `mod` 2 /= 0] 

-- learning rate 
gamma = 0.04 

g :: (Integral s, Fractional s) => s -> s -> s 
g m c = cost m c x_ y_ 

deriv (Dual _ x') = x' 

z_ = (0.1, 0.1) : map h z_ 

h (c, m) = (c - gamma * cd, m - gamma * md) where 
    cd = deriv $ g (constD m) $ idD c 
    md = deriv $ flip g (constD c) $ idD m 

-- check for convergence 
main = do 
    take 2 $ drop 1000 $ map (\(c, m) -> cost m c x_ y_) z_ 
    take 2 $ drop 1000 $ z_ 

где x_ тестовых данных и y_ массивы и гамма скорость обучения скаляр.

[2]: Два поля дуального объекта фактически является сопряженным один с другими, если мы видим производную как оператор

+0

Если кто-нибудь натыкается на этом примере, ошибка заключается в использовании список понимание для создания x_ данных и y_. Если вы заявляете вместо этого свои записи явно, например. x _ = [1,2,4,10,2] и т. д., It Just Works – ocramz

ответ

2

Во-первых, (Integral s, Fractional s) не имеет смысла; Integral предназначен для эвклидовых доменов (те, которые имеют div и mod), а Fractional - для полей (те, которые имеют /). Если у вас есть истинное деление, все ваши остатки будут равны нулю ....

Я думаю, что проблема заключается в том, чтобы попытаться отфильтровать нечетные числа. y_. Haskell 98 определяет «ступенчатую» форму диапазона для чисел, поэтому вы можете написать y_ как [1,3..19]. Это должно позволить использовать y_ по типу [Dual], что должно позволить использовать g без необходимости ограничения Integral.

Edit: Ørjan Йохансен указывает на то, что вам нужно Enum экземпляр для Dual, а также, что на самом деле довольно легко реализовать (это является довольно стандартным для числовых типов, я в основном скопированы экземпляр GHC для Double (который идентичен его экземпляр для Float, например)):

instance Enum Dual where 
    succ x    = x + 1 
    pred x    = x - 1 
    toEnum    = fromIntegral 
    fromEnum (Dual x _) = fromEnum x 
    enumFrom   = numericEnumFrom 
    enumFromTo   = numericEnumFromTo 
    enumFromThen  = numericEnumFromThen 
    enumFromThenTo  = numericEnumFromThenTo 
+0

Ограничение типа было выведено, не смотрите на меня .. Я попытался опустить его, но такую ​​же ошибку. В то время я понял это! текущее определение g делает его зависимым от типа входных данных x_, y_. В частности, '[1..4] :: (Num t, Enum t) => [t]', тогда как '[1,2,3,4] :: Num t => [t]' Я взял ярлык объявления данных в виде списков, но он излишне перенапрягает код. Идите фигуру. Спасибо за подсказку! – ocramz

+1

@ocramz, нет, это 'mod', который сдерживает код, как сказал jcast. Вот откуда взялось ваше ограничение «Интеграл». –

+0

'[1,3..19]' не будет работать, поскольку 'Dual' также не имеет экземпляра« Enum ». –

2

В исходном коде, я не вижу тип подписи для g. В вашем коде, вы написаны специально

g :: (Integral s, Fractional s) => s -> s -> s 

сообщение об ошибке говорит, что нет Integral экземпляра для Dual. Код вручную определяет экземпляры для Num и Fractional, но не Integral.

Я действительно не уверен, почему g должно быть Integral. Если убрать это ограничение, то код может работать даже ...

EDIT: кажется экземпляр Integral необходим из-за ваше использование mod для генерации тестовых данных. Я не уверен, что делает этот огромный блок кода, но я подозреваю, что если вы примените fromIntegral, чтобы преобразовать все (скажем) Double, тогда это может сработать.

(Я подозреваю, что сделать Dual экземпляр Integral, вероятно, не является тем, что предполагали авторы оригинала. И снова я действительно не понимаю код, поэтому ...)

+0

К сожалению, удаление ограничения не меняет вопросов. Я попытаюсь сделать «Двойной» экземпляр «Интеграл». Может ли быть так, что прагма {- # LANGUAGE NoMonomorphismRestriction # -} каким-то образом вмешивается? Я только что скопировал оригинал, но не посмотрел его определение. – ocramz

+1

Вы не можете конвертировать только в 'Double', это должно быть' Dual'. Но 'fromIntegral' должен работать, так как' Dual' имеет экземпляр 'Num'. –

+0

@ ØrjanJohansen Совершенно верно ... мой плохой. – MathematicalOrchid

Смежные вопросы