Нужна помощь в понимании передачи функций в Python

Я пытаюсь научить себя Python, работая над некоторыми проблемами, с которыми я столкнулся, и мне нужна помощь в понимании того, как передавать функции.

Допустим, я пытаюсь предсказать температуру завтра на основе сегодняшней и вчерашней температуры, и я написал следующую функцию:

def predict_temp(temp_today, temp_yest, k1, k2):
    return k1*temp_today + k2*temp_yest

И я также написал функцию ошибок, чтобы сравнить список прогнозируемых температур с фактическими температурами и вернуть среднюю абсолютную ошибку:

def mean_abs_error(predictions, expected):
    return sum([abs(x - y) for (x,y) in zip(predictions,expected)]) / float(len(predictions))

Теперь, если у меня есть список дневных температур для некоторого интервала в прошлом, я могу увидеть, как моя функция прогноза работала бы с конкретными параметрами k1 и k2, например:

>>> past_temps = [41, 35, 37, 42, 48, 30, 39, 42, 33]
>>> pred_temps = [predict_temp(past_temps[i-1],past_temps[i-2],0.5,0.5) for i in xrange(2,len(past_temps))]
>>> print pred_temps
[38.0, 36.0, 39.5, 45.0, 39.0, 34.5, 40.5]
>>> print mean_abs_error(pred_temps, past_temps[2:])
6.5

Но как мне разработать функцию, которая минимизирует мои параметры k1 и k2 моей функции pred_temp с учетом функции ошибок и моих данных past_temps?

В частности, я хотел бы написать функцию минимизации (args *), которая принимает функцию прогнозирования, функцию ошибок, некоторые обучающие данные и использует некоторый метод поиска / оптимизации (например, градиентный спуск) для оценки и возврата значений k1 и k2, которые минимизируют мою ошибку с учетом данных?

Я не спрашиваю, как реализовать метод оптимизации. Предположим, я смогу это сделать. Скорее, я просто хотел бы знать, как передать мои функции прогнозирования и ошибки (и мои данные) моей функции минимизации и как сообщить моей функции минимизации, что она должна оптимизировать параметры k1 и k2 , чтобы моя функция минимизации могла автоматически выполните поиск множества различных настроек k1 и k2, применяя мою функцию прогнозирования с этими параметрами каждый раз к данным и ошибке вычислений (как я делал вручную для k1 = 0,5 и k2 = 0,5 выше), а затем возвращайте наилучшие результаты.

Я хотел бы иметь возможность передавать эти функции, чтобы я мог легко менять различные функции прогнозирования и ошибок (которые отличаются не только настройками параметров). Каждая функция прогнозирования может иметь разное количество свободных параметров.

Моя функция минимизации должна выглядеть примерно так, но я не знаю, что делать дальше:

def minimize(prediction_function, which_args_to_optimize, error_function, data):
    # 1: guess initial parameters
    # 2: apply prediction function with current parameters to data to compute predictions
    # 3: use error function to compute error between predictions and data
    # 4: if stopping criterion is met, return parameters
    # 5: update parameters
    # 6: GOTO 2

Изменить: это так просто ?? Это не весело. Я возвращаюсь на Java.

Что касается более серьезного замечания, я думаю, что я также зацикливался на том, как использовать разные функции прогнозирования с разным количеством параметров для настройки. Если я просто возьму все свободные параметры в виде одного кортежа, я смогу сохранить форму функции такой же, чтобы ее было легко передавать и использовать.

Ответов (3)

Решение

Вот пример того, как передать функцию другой функции. apply_func_to примет функцию f и число в num качестве параметров и return f(num) .

def my_func(x):
    return x*x

def apply_func_to(f, num):
    return f(num)

>>>apply_func_to(my_func, 2)
4

Если вы хотите быть умным, вы можете использовать лямбда (также анонимные функции). Они позволяют передавать функции «на лету» без необходимости определять их отдельно.

>>>apply_func_to(lambda x:x*x, 3)
9

Надеюсь это поможет.

Как отмечают Дэвид и Ил-Бхима , функции могут передаваться другим функциям, как и любой другой тип объекта. Когда вы передаете функцию, вы просто вызываете ее, как обычно. Иногда люди ссылаются на эту способность, говоря, что функции в Python являются первоклассными . На чуть более высоком уровне детализации вы должны думать о функциях в Python как о одном из типов вызываемых объектов . Другой важный тип вызываемых объектов в Python - это объекты классов; в этом случае вызов объекта класса создает экземпляр этого объекта. Эта концепция подробно обсуждается здесь .

Как правило, вы, вероятно, захотите использовать функцию позиционного и / или ключевого аргумента Python, как описано здесь . Это позволит вам написать общий минимизатор, который может минимизировать функции прогнозирования, принимающие различные наборы параметров. Я написал пример - он сложнее, чем хотелось бы (использует генераторы!), Но он работает для функций прогнозирования с произвольными параметрами. Я замалчил некоторые детали, но это должно помочь вам начать:

def predict(data, k1=None, k2=None):
    """Make the prediction."""
    pass

def expected(data):
    """Expected results from data."""
    pass

def mean_abs_err(pred, exp):
    """Compute mean absolute error."""
    pass

def gen_args(pred_args, args_to_opt):
    """Update prediction function parameters.

    pred_args : a dict to update 
    args_to_opt : a dict of arguments/iterables to apply to pred_args

    This is a generator that updates a number of variables 
    over a given numerical range.  Equivalent to itertools.product.

    """

    base_args = pred_args.copy() #don't modify input

    argnames = args_to_opt.keys()
    argvals = args_to_opt.values()
    result = [[]]
    # Generate the results
    for argv in argvals:
        result = [x+[y] for x in result for y in argv]
    for prod in result:
        base_args.update(zip(argnames, prod))
        yield base_args

def minimize(pred_fn, pred_args, args_to_opt, err_fn, data):
    """Minimize pred_fn(data) over a set of parameters.

    pred_fn : function used to make predictions
    pred_args : dict of keyword arguments to pass to pred_fn
    args_to_opt : a dict of arguments/iterables to apply to pred_args
    err_fn : function used to compute error
    data : data to use in the optimization

    Returns a tuple (error, parameters) of the best set of input parameters.
    """
    results = []
    for new_args in gen_args(pred_args, args_to_opt):
        pred = pred_fn(data, **new_args) # Unpack dictionary
        err = err_fn(pred, expected(data))
        results.append((err, new_args))
    return sorted(results)[0]

const_args = {k1: 1}
opt_args = {k2: range(10)}
data = [] # Whatever data you like.
minimize(predict, const_args, opt_args, mean_abs_err, data)

Передача функций в Python проста, вы просто используете имя функции как переменную, которая содержит саму функцию.

def predict(...):
    ...

minimize(predict, ..., mean_abs_error, ...)

Что касается остального вопроса: я бы посоветовал взглянуть на то, как SciPy реализует это в качестве модели. По сути, у них есть функция, leastsq которая минимизирует сумму квадратов остатков (я полагаю, вы знаете, что такое минимизация по методу наименьших квадратов ;-). То, что вы передаете, leastsq - это функция для вычисления остатков, начальные предположения для параметров и произвольный параметр, который передается вашей функции вычисления остатков (закрытие), которая включает в себя данные:

# params will be an array of your k's, i.e. [k1, k2]
def residuals(params, measurements, times):
    return predict(params, times) - measurements

leastsq(residuals, initial_parameters, args = (measurements, times))

Обратите внимание, что SciPy на самом деле не заботится о том, как вы получаете остатки. measurements Массив просто передаются в неизмененном виде в вашу residuals функцию.

Я могу посмотреть пример, который я сделал недавно, если вам нужна дополнительная информация - или, конечно, вы можете найти примеры в Интернете, но, по моему опыту, они не так ясны. Конкретный фрагмент кода, который я написал, хорошо подошел бы вашему сценарию.