Я изучал связку [tenorflow haskell .Однако я изо всех сил пытаюсь заставить базовый пример линейной регрессии из readme работать должным образом: он расходится с тем, что кажется очень простой задачей: выучить строку y = 2x+3
, иначе Простая линейная регрессия с использованием градиентного спуска.Я сделал github repo , содержащий исполняемый пример (используя stack
+ nix
), но вот суть этого:
-- | compute simple linear regression, using gradient descent on tensorflow
simpleLinearRegression' :: Float -> [Float] -> [Float] -> IO (Float, Float)
simpleLinearRegression' learningRate x y =
TFL.withEventWriter "test.log" $ \eventWriter -> TF.runSession $ do
let x' = TF.vector x
y' = TF.vector y
b0 <- TF.initializedVariable 0
b1 <- TF.initializedVariable 0
let yHat = (x' * TF.readValue b1) + TF.readValue b0
loss = TFC.square $ yHat - y'
TFL.histogramSummary "losses" loss
TFL.scalarSummary "error" $ TF.reduceSum loss
TFL.scalarSummary "intercept" $ TF.readValue b0
TFL.scalarSummary "weight" $ TF.readValue b1
trainStep <- TF.minimizeWith (TF.gradientDescent learningRate)
loss
[b0, b1]
summaryT <- TFL.mergeAllSummaries
forM_ ([1 .. iterations] :: [Int64]) $ \step -> do
if step `mod` logEveryNth == 0
then do
-- TF.run_ trainStep
((), summaryBytes) <- TF.run (trainStep, summaryT)
(TF.Scalar beta0, TF.Scalar beta1) <- TF.run
(TF.readValue b0, TF.readValue b1)
-- liftIO $ putStrLn $ "Y = " ++ show beta1 ++ "X + " ++ show beta0
let summary = decodeMessageOrDie (TF.unScalar summaryBytes)
TFL.logSummary eventWriter step summary
else TF.run_ trainStep
(TF.Scalar b0', TF.Scalar b1') <- TF.run (TF.readValue b0, TF.readValue b1)
return (b0', b1')
Это в основном код из файла readme.где я превратил learningRate
в параметр и добавил некоторые записи для тензорной доски (хотя это не помогает мне понять проблему).
Есть небольшой набор тестов, демонстрирующий расходящуюся ситуацию:
linearRegressionSpec :: Spec
linearRegressionSpec = do
-- n = 6 vs n = 7 on same x range: PASS vs FAIL (beta0, beta1: NaN)
linearRegressionTest 0.01 3 2 $ equidist 6 1 6
linearRegressionTest 0.01 3 2 $ equidist 7 1 6
-- n = 6, larger x range: PASS vs FAIL
linearRegressionTest 0.01 3 2 $ equidist 6 1 6
linearRegressionTest 0.01 3 2 $ equidist 6 1 7
-- n = 12 vs n = 13: PASS vs FAIL (beta0, beta1: NaN) (reduced learning rate)
linearRegressionTest 0.005 3 2 $ equidist 12 1 6
linearRegressionTest 0.005 3 2 $ equidist 13 1 6
-- another one, different learning rate, but diverges with growing sample size.
-- this is the learning rate used in the Readme.
linearRegressionTest 0.001 3 2 $ equidist 26 1 10
linearRegressionTest 0.001 3 2 $ equidist 27 1 10
-- n = 99 vs n = 100, ranging from -1 to 1: PASS vs FAIL (beta1 estimate = 0)
-- this one is different: the failing case does not diverge.
linearRegressionTest 0.01 3 2 $ equidist 99 (-1) 1
linearRegressionTest 0.01 3 2 $ equidist 100 (-1) 1
linearRegressionTest 0.001 3 2 $ equidist 100 (-1) 1
-- initial goal: fit linear regression on advertising data from ISLR, Chapter 3.1
islrOLSSpec
-- | produce a list of n values equally distributed over the range (minX, maxX)
equidist :: Int -> Float -> Float -> [Float]
equidist n minX maxX =
let n' = fromIntegral $ n - 1
f k = ((n' - k) * minX + k*maxX) / n'
in f <$> [0 .. n']
roughlyEqual :: (Num a, Ord a, Fractional a) => a -> a -> Bool
roughlyEqual expected actual = 0.01 > abs (expected - actual)
-- switching between different implementations
-- fitFunction = Readme.fit
fitFunction = simpleLinearRegression'
-- fitFunction = simpleLinearRegressionMMH
linearRegressionTest :: Float -> Float -> Float -> [Float] -> Spec
linearRegressionTest learnRate beta0 beta1 xs = do
let ys = (\x -> beta1*x + beta0) <$> xs
it ("linear regression on one variable, n = " ++
show (length xs) ++ ", range (" ++ show (head xs) ++ ", " ++ show (last xs) ++ ")") $ do
(beta0Hat, beta1Hat) <- fitFunction learnRate (fromList xs) (fromList ys)
beta0Hat `shouldSatisfy` roughlyEqual beta0
beta1Hat `shouldSatisfy` roughlyEqual beta1
Из чего я узнаю:
- уменьшение скорости обучения улучшает конвергенцию
- увеличение размера выборки уменьшает конвергенцию
- с учетом дисперсии входных данныхпеременная уменьшает сходимость
Тем не менее, я озадачен поведением.Я не ожидал бы, что расхождение является такой большой проблемой, что мне кажется очень маленьким набором данных.
Вопросы:
- Что-то не так с кодом?
- Если нет, как вы можете определить, можно ли применять градиентный спуск и когда?
- Существуют ли стратегии смягчения (например, нормализации данных)?
- Хотя я понимаю взаимосвязь между скоростью обучения и конвергенцией, я удивлен влиянием размера выборки и диапазона входных переменных.Существует ли какая-либо формула для оценки хорошей скорости обучения на основе входных данных?
Я начал это исследование после попытки подбора исходного примера простой линейной регрессии из главы 3.1 Введение в статистическое обучение..Я могу заставить пример (регрессия продаж на ТВ) сходиться со скоростью обучения 0.0000001
, что требует очень большого количества шагов.