Getting More Behavior Out of Numbers

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 22

ОТЧЕТ

о переводе статьи
« Getting More Behavior out of Numbers»
(18 000 печатных знаков)

Abstract

The article called «Getting More Behavior out of Numbers» gives us some examples of
numeric algorithms, which can be useful in game AI. Author explains, why traditional “if-then”
method is outdated and demonstrates readers, how to use simple linear equations, and
exponential equations to simplify the decision-making. The article will be useful to novice
programmers and people interested in AI mathematical algorithms.

Summary

This article called «Getting More Behavior out of Numbers» was written by Dave Mark
and published in 2011 in Game Developer Magazine.

It consists of many parts, which are dedicated to some mathematical algorithms used
in game AI development.

In the first part author explains the connection between numbers and programmers.
He tells us, that traditional “if-then” method is outdated and offers a look at other methods.

The second part describes how we can homogenize data for simplifying work with it.
Normalization can help to detach the comparison code from any tweaking we do with the
actual construction of the values themselves. Also, author explains the meaning of the term
"utility".

The next big part is dedicated to some algorithms and operations on them, such as
linear equations, exponential equations and logistic functions with utility as one of the
parameters. With increasing complexity of the calculations, necessity for methods yielding more
accurate results grows too.

In conclusion, author notes, that the result of all of this is that we can create very
sophisticated response curves that translate our raw values into meaningful utility values. Also,
2

because these end products are normalized, we can now easily compare and contrast them
with other results. So, these methods can be used to create advanced AI in different game
genres.

Questions

 Special
1. What type of algorithm can we use to define “soft thresholds” between values?

2. What do you know about normalization?

3. What does the term “utility” mean?

 “Do you think” question


4. Why do you think numbers are important for computer games?

 Alternative

5. When were the first computer RPG created: in 1980s or in 2000s?

 General
6. Does this article focus on computer graphics?

 Tag-question

7. You can’t create game AI if you don’t use logistics functions, can you?
3

8. AI is one of the key elements of the game, isn’t it?

Examples on grammatical phenomena

«Conditional sentences *»

1. If I see the player, attack the player – если я вижу игрока, то атакую его.
2. If you are faced with one final enemy […], you don’t automatically reach for your reload
– если вы остались наедине с последним врагом – вы не будут автоматически
перезаряжать оружие.
3. If we were to change it to 1.2, for example, the rate of descent would increase
significantly – если мы изменим его до значения, например, 1.2, то скорость спуска
значительно возрастет.
4. If you tip it in the slightest, you run the risk of sending things tumbling – если вы
нечаянно зацепите не с той стороны, то все содержимое тут же вывалится.
5. If you assume for the moment that any given state could potentially transition to any of
the other states, the number of transitions increases quickly – Если исходить из
предположения о том, что из каждого заданного состояния мы можем попасть в
любое другое состояние, то число переходов быстро увеличится.
6. If we were to add a State E, we would have to edit states A-D to add the transitions to E
– для добавления состояния E мы должны изменить состояния A – D для
добавления переходов к новому состоянию.
7. If I hear a gunshot, it really doesn’t matter what I’m doing at the time – если я слышу
выстрел, то не имеет значения, чем я занят в это время.
8. If we add a new behavior, we add the code to call it in one place – если мы добавляем
новое поведение, то мы добавляем код для его вызова в одно место.
9. If the goal is “kill player”, a planner might discover that one method of satisfying that
goal is to “shoot player”. – если целью является “убить игрока”, планировщик может
определить, что одним из методов достижения этой цели будет “пристрелить
игрока”
10. If you have GDC Vault access, you can view my AI Summit lectures – если у вас есть
доступ к GDC Vault, вы можете посмотреть мои лекции с AI Summit.

* All phrases are translated in the book context


4

Term list

1. Normalization Пересчет данных в процентах – в интервале 0-1


2. Utility Польза (полезность) - степень важности,
актуальности предмета или явления

Keywords
Numbers, normalization, utility, exponent, logistic function.

« Getting More Behavior out of Numbers»


Dave Mark for the January 2011 edition of Game Developer Magazine.
http://intrinsicalgorithm.com/IAonAI/2011/12/getting-more-behavior-out-of-
numbers-gdmag-article/

We have long been used to numbers in games. Thousands of years ago, when people first started
playing games, the simple act of keeping score was dealt with in terms of numbers. Even before
games, when people had to simply scratch by for food, numbers were an integral part. From how
many rabbits the hunter killed to how many sling stones he had left in his pouch, numbers have
been a part of competitive activity for all of history.

Coming back to the present, numbers are everywhere for the game player. Some are concrete
values that have analogs in real life: How much ammo do we have? How many resources will
this take to build? What is the range of that weapon? Some are a little more nebulous—if not
contrived altogether: What level am I? What condition are my gloves in? What armor bonus does
this magic ring afford? And, although the medical community might be a bit startled by the
simplicity, games often parade a single number in front of us to tell us how much “life” we have.
(“He’s only mostly dead.”) Suffice to say that we have educated our gaming clientele to forgive
this intentional shredding of the coveted suspension of disbelief. Even though some games have
attempted to obscure this relationship by removing some of the observable references to this
numeric fixation, gamers still recognize that the numbers are still there, churning away behind
the Wizard’s curtain.

Numbers in Games

As programmers, our fixation with numbers is not coincidental. After all, along with logic,
mathematics is the language of our medium. Computers excel in their capacity to crunch all
these numbers. They’re in their element, so to speak. This capacity is a primary reason that the
pen-and-paper RPGs of the 70s and 80s so energetically made the transition to the computer
5

RPGs of the 80s and beyond. Resolving combat in Ultima (1980) and Wizardry (1981) was far
swifter that shuffling through charts, tables, and scribbles on scratch paper in D&D.

So numbers in games aren’t going away any time soon—whether in overt use or under the hood.
The interesting, and sometimes even disconcerting thing, is that they aren’t used more often.
Even with all of the advances and slick architectures that artificial intelligence programmers use,
we too often fall back on the most simple of logical constructs to make our decision code. The
most obvious example is the venerable if/then statement. Testing for the existence of a criterion
is one of the core building blocks of computational logic. “If I see the player, attack the player.”
Certainly, this sort of binary simplicity has its place. Left to itself, however, it can fall woefully
short of even being adequate.

The answer lies not in the existence of numbers in our world, but what those numbers represent
and, ultimately, how we use them.

We could extend the above example by putting a number in the equation such as “If the player is
< 30 [units of distance] away from me, attack him.” But really what have we gained? We are still
testing for the existence of a criterion – albeit one that is defined elsewhere in the statement or in
the program. After all, “see the player” and “player < 30” are simply functions. There is still a
razor edge separating the two potential states of “idle” and “attack”. All subtlety is lost.

So how might we do things differently? The answer lies not in the existence of numbers in our
world, but what those numbers represent and, ultimately, how we use them.

Looking Inward

Stop for a moment and do a self-inventory. Right now, as you sit reading this column, are you
hungry? Sure, for the sake of simplicity, you may answer “yes” or “no”. However, there is
usually more to it than that. When my daughter was younger, she tended to cease using “hungry”
when she was no longer empty. (This usually meant that she ate two or three bites only to come
back wanting more about 20 minutes later.) I, on the other hand, could easily see myself defining
“hungry” as “no longer full”. My wife has the penchant for answering somewhat cryptically, “I
could be.” (This is usually less convenient than it sounds.)

All of this makes sense to us on an intuitive level. “Hunger” is a continuum. We don’t just
transition from “completely full” to “famished”—it is a gradient descent. What we do with that
information may change, however, depending on where we are in that scale. For example, we
can make judgment decisions such as “I’m not hungry enough to bother eating right now… I
want to finish writing this column.” We can also make comparative judgments such as “I’m a
little hungry, but not as much as I am tired.” We can even go so far as use this information to
make estimates on our future state: “I’m only a little hungry now, but if I don’t eat before I get
on this flight, my abdomen will implode somewhere over Wyoming. Maybe I better grab a snack
while I can.”

Compare this to how the AI for game characters is often written. The subtlety of the differences
in value seems to be lost on them. Soldiers may only reload when they are completely out of
ammunition in their gun despite being in the proverbial calm before the storm. Sidekicks may
elect to waste a large, valuable health kit on something that amounts to a cosmetically
unfortunate skin abrasion. The coded rules that would guide the behaviors above are easy for us
to infer:
6

if (MyAmmo == 0)
{
Reload;
}

if (MyHealth < 100)


{
UseHealthKit;
}

Certainly we could have changed the threshold for reloading to MyAmmo <= 5, but that only
kicks the can down the road a bit. We could just have easily found our agent in a situation where
he had 6 remaining bullets and, to co-opt a movie title, all was quiet on the western front.
Dude… seriously—you’re not doing anything else right now, might as well shove some bullets
into that gun. However, an agent built to only pay homage to the Single Guiding Rule of
Reloading would stubbornly wait until he had 5 before reaching for his ammo belt.

Additionally, there are other times when a rule like the above could backfire (so to speak) with
the agent reloading too soon. If you are faced with one final enemy who needs one final shot to
be dispatched, you don’t automatically reach for your reload when you have 5 bullets. You finish
off your aggressor so as to get some peace and quiet for a change.

Needless to say, these are extraordinarily simplistic examples, and yet most of us have seen
behavior similar to this in games. The fault doesn’t rest in the lack of information—as we
discussed, often the information that we need is already in the game engine. The problem is that
AI developers don’t leverage this information in creative ways that are more indicative of the
way real people make decisions. Very rarely do we humans, make a decision based solely on a
single criteria. As reasonable facsimiles of the hypothetical Homo economicus, we are wired to
compare and contrast the inputs from our environment in a complex dance of multivariate
assessments leading us to conclusions and, ultimately, to the decisions we make. The trick, then,
is to endow our AI creations with the ability to make these comparisons of relative merit on their
own.

Leveling the Field

So how do we do this? The first step is to homogenize our data in such a way as to make
comparisons not only possible, but simple. Even when dealing with concrete numbers, it is
difficult to align disparate scales.

Consider a sample agent that may have maximums of 138 health points and 40 shots in a fully-
loaded gun. If at a particular moment had 51 health and 23 bullets in the gun, we wouldn’t
necessarily know at first glance which of the two conditions is more dire. Most of us would
instinctively convert this information to a percentage—even at a simple level. E.g. “He has less
than half health but more than half ammo.” Therein lays our first solution… normalization of
data.

My gentle readers should be familiar with the term normalization in principle, if not the exact
usage in this case. Put simply, it is restating data as a percent—a value from 0 to 1. In the case
above, our agent’s health was 0.369 and his ammo status 0.575. Not only does viewing the data
this way allow for more direct comparisons—e.g. 0.575 > 0.369—but it has the built-in
flexibility to handle changing conditions. If our agent levels up, for example and now has 147
health points, we do not have to take this change into consideration into our comparison formula.
7

Our 51 health above is now 0.347 (51 ÷ 147) rather than 0.369. We have detached the
comparison code from any tweaking we do with the actual construction of the values themselves.

But What Does It Mean?

Normalization, however, only sets the state for the actual fun stuff. Simply comparing
percentages between statuses like “health” and “ammo” usually isn’t sufficient to determine the
relative importance of their values. For example, I posit that being at 1% health is measurably
more urgent than being down to 1% ammo. Enter the concept of utility.

Utility is generally a different measure than simply value. Value, such as our normalized
examples above, expresses a concrete number. Utility, on the other hand, expresses a concept. In
this case, the concept we are concerned with is “urgency”. While it is related to the concrete
values of health and ammo, urgency is its own animal.

The easiest way of doing this is by creating a “response curve”. Think of passing the actual
numbers through a filter of “what does this value mean to me?” That is what converting value to
utility is like. This filter is usually some sort of formula that we use to massage the raw data.
Unlike a lookup table of ranges (such as “ammo ≤ 5”), we have the benefit of continuous
conversion of data. We will see how this benefits us later.

The selection of the formula needs to take into consideration specific contour of the translation
from value to utility. There are innumerable functions that we can use, but they are all built out
of a few simple building blocks. Each of these blocks can be stretched and squished in and of
themselves, and combining them together results in myriad combinations.

The first filter that we can run our numbers through is simply a linear conversion. (For these
examples, I will simply use the standard x and y axis. I’ll occasionally through in an example of
what they could represent.) Consider the formula:

y = 0.8x + 2

This results in a line running from our co-maximum values of (1.0, 1.0) and arrives at y = 0 when
x = .2. (See Figure 1.) Put another way, we want a steady descent in our utility (y) at a somewhat
quicker rate than the decent of the actual value (x). We could have done something similar by
changing the formula to:

y = 0.8x

As this point, the line extends from (1.0, 0.8) to (0, 0).
8

Figure 1

Obviously changing the slope of the line—in this case, 0.8—would change the rate that the
utility changes along with the value (x). If we were to change it to 1.2, for example, the rate of
descent would increase significantly. (See Figure 2.)

y – 1.2x − .2

It’s worth noting here that these formulas are best served by being combined with a clamping
function that ensures that 0.0 ≤ y ≤ 1.0. When we take that into consideration, we have another
feature to identify here: when x < 0.2, y is always equal to 0.

On the other hand, consider the similar formula:

y = 1.2x

This exhibits the same effect with the exception that now the “no effect” zone is when x > 0.8.
That is, the utility doesn’t start changing until our value < 0.8.

These effects are useful for expressing the situations where we simply do not care about changes
in the utility at that point.

Figure 2
9

Enter the Exponent

The simple formulas above merely set the stage for more advanced manipulations. For example,
imagine a scenario where the meaning of something starts out as “no big deal”, yet becomes
important at an increasing rate. The state of the ammo in our gun that we illustrated above
makes an excellent example. In this case, the value is simply the number of shots remaining
whereas the utility value is our urgency to reload.

Analyzing this outside the realm of the math—that is, how would we behave—gives us clues as
to how we should approach this. Imagine that our gun is full (let’s assume 40 shots for
convenience)… and we fire a single shot. All other things being equal, we aren’t likely to get too
twitchy about reloading. However, firing the last shot in our gun is pretty alarming. After all,
even having 1 shot left was a heckuva lot better than having none at all. At this point, it is helpful
to start from those two endpoints and move toward the center. How would we feel about having
35 shots compared to 5? 30 compared to 10? Eventually, we will start to see that we only really
become concerned with reloading when we our ammo drops gets down to around 20 shots—at
that below that, things get urgent very quickly!

In a simple manner, this can be represented by the following formula:

y = (x − 1)2

As we use up the ammo in our gun (x), there is still an increase in the utility of reloading, but the
rate that the utility increases is accelerating. (See Figure 3.) This is even more apparent when we
change the exponent to higher values such as 3 or 4. This serves to deepen the curve
significantly. Note that a version of the formula with odd exponents would require an absolute
value function so as to avoid negative values.

Figure 3

Another quick note about manipulating these formulas. We could turn the above curves “upside
down” by arranging it as follows:

y = (1 − x)2

Looking at the chart (Figure 4) shows that this version provides a significantly different behavior
—an agent who has a very low tolerance for having an empty gun, for example!
10

Figure 4

By manipulating how the function is arranged, we can achieve many different arrangements to
suit our needs. We can shift the function on either axis much as we did the linear equations
above, for example. (See Figure 5.)

Figure 5

We can specify where we want the maximum utility to occur—it doesn’t have to be at either end
of the scale. For example, we might want to express a utility for the optimal distance to be away
from an enemy based on our weapon choice. (See Figure 6.)

y = 2(1 − |(x − 0.3)|2)


11

Figure 6

Soft Thresholds

While we can certainly get a lot of mileage out of simple linear and exponential equations, one
final class of formulas is very useful. Sigmoid functions, particularly the logistic function, can be
used to define “soft thresholds” between values. In fact, logistic functions are often used as
activation functions in neural networks. Their use here, however, is much less esoteric.

The base logistic function is:

y = 1 / (1 + e-x)

While the base of the natural logarithm, e, is conspicuous in the denominator of the fraction, it is
really optional. We can certainly use the approximation of 2.718 in that space, 2, 3, or any other
number. In fact, by changing the value for e, we can achieve a variety of different slopes to the
center portion of the resulting curve. As stated, however, the formula graphs out as shown in
Figure 7.

Figure 7

Notice that, unfortunately, the graph’s natural range is not 0–1 as with our other examples. In
fact, the range of the graph is infinite in that it asymptotically approaches both y=0 and y=1. We
12

can apply some shifting to get it to fit the 0–1 range, however, so that we can use it with
normalized values of x. We can also change the area of the graph where the threshold occurs by
changing what we are adding to the exponent.

y = 1 / (1 + e–(10x – 5))

Comparing and Contrasting

The end result of all of this is that we can create very sophisticated response curves that translate
our raw values into meaningful utility values. Also, because these end products are normalized,
we can now easily compare and contrast them with other results. Going back to the examples I
cited early on, we can decide how hungry we are in relation to other feelings such as tired (or too
busy finishing a last-minute column for a magazine). In fact, we can line up dozens—or even
hundreds—of utilities for various feelings or potential actions and select from among them using
techniques as simple as “pick the highest” to seeding weight-based randoms.

Compare this to what we would have to do were we not to use the normalized utility values. In
our hungry/tired/busy example, we normally would have had to construct a multi-part condition
to define each portion of our decision. For example:

If ( (Hungry > 5) && (Tired < 3) && (Busy < 7) ) then


{
Eat();
}

If ( (Hungry < 4) && (Tired > 6) && (Busy < 3) )then


{
Sleep();
}

Even if the above values were normalized (i.e. between 0 and 1), the complexity explosion in
simply comparing the different possible ranges and mapping them to the appropriate outcome
would get out of hand quickly. And that’s just with 3 inputs and 3 outputs! By converting from
value to utility, we massage what the data “means to us” inside each response curve. We now
can feel comfortable that a direct comparison of the utilities will yield which item is truly the
most important to us.

The system is extensible to grand lengths as well. If we want to include a new piece of
information or a new action to take into account, we simply need to add it to a list. Because all
the potential actions scored and sorted by their relative benefit, we will automatically take
newcomers into stride without much (if any) adjustment to the existing items.

If calculating and measuring all of these feelings and desires is starting to sound a lot like The
Sims, it is not a coincidence. The Sims is an excellent (but not the only) example of how
complex utility-based functions can be used to simulate fairly reasonable, context-dependent,
decision processes in agents. Richard Evans has spoken numerous times at GDC on this very
subject. I wholeheartedly recommend reading his papers and viewing his slides on the subject.

The uses of these methods aren’t limited to that genre, however. Strategy games, in particular,
lend themselves to more nuanced calculation. Even in modern shooters and RPGs, agents are
expected to make increasingly more believable decisions in environments that contain
significantly more information. Our AI no longer has the luxury of simply leaning on “if I see
13

the player, shoot him!” as its sole guideline and building static rulesets that address all the
possible permutations of world state gets brittle at an exponential pace.

However, as I’ve illustrated (ever so briefly) the inclusion of some very simple techniques lets us
step away from these complicated, often contrived, and sometimes even contradictory rulesets. It
also allows us, as AI designers, to think in familiar terms of “how much”—the same terms that
we often use when we think of our own (human) states. The numbers we need are there already.
The magic is in how we use them.

Getting More Behavior out of Numbers

Мы уже давно привыкли к числам в играх. Тысячи лет назад, когда люди впервые
начали играть, простое действие подсчета очков уже было связано с числами. Задолго до
игр, когда люди просто должны были заботиться о пропитании, числа были неотъемлемой
частью их жизни. Сколько кроликов убил охотник, сколько камней для пращи осталось в
его сумке, числа всегда были частью соревновательной деятельности людей.

Возвращаясь к нашим дням – игроки всюду сталкиваются с числами. Некоторые из


них – конкретные значения, имеющие аналоги в реальной жизни: сколько патронов у нас
осталось? Сколько ресурсов потребуется, чтобы построить это здание? Какой радиус
действия этого оружия? Некоторые немного более туманны, если не вымышлены: Какого
я уровня? В каком состоянии мои перчатки? Какой бонус к броне дает это волшебное
кольцо? И, хотя медики могут быть поражены простотой этого подхода, игры часто
демонстрируют на экране простое число, которое показывает, сколько “жизней” у нас
есть. Можно сказать, что мы воспитываем наших клиентов-игроков так, чтобы они не
обращали внимания на это намеренное нарушение вовлеченности в игровой процесс. Хотя
некоторые игры пытаются скрыть видимые признаки использования числовых
характеристик, игроки отмечают, что номера никуда не исчезли, и просто скрылись за
завесой, созданной разработчиками.
14

Числа в играх

Наша, как программистов, связь с числами не случайна. Ведь, наряду с логикой,


математика – язык нашей среды. Компьютеры преуспели в перемалывании чисел, они, так
сказать, находятся в своей стихии. Это способность была основной причиной того, почему
настольные RPG с карандашом и бумагой 70х-80х годов в конце 80х уступили место
компьютерным. Вычисление хода боя на компьютере в Ultima (1980) и Wizardry (1981)
было гораздо быстрее, чем переписывание таблиц, графиков и каракулей на черновиках в
настольном D&D.

Разумеется, в ближайшее время числа, в явном или скрытом виде, никуда не


исчезнут. Интересен, и иногда приводит в замешательство тот факт, что они не
используются так часто, как могли бы. Несмотря на все достижения и варианты
архитектур, используемые разработчиками ИИ, мы очень часто скатываемся к
применению простейших логических конструкций в своем коде. Самый очевидный
пример – древнее, как мир, логическое построение если-то. Тестирование на соблюдение
определённого условия служит одним из основных блоков вычислительной логики. “Если
я вижу игрока – я атакую игрока”. Конечно имеет место определенная двоичная простота,
но сама по себе она может привести к печальным последствиям, несмотря на свою
адекватность.

Мы могли бы расширить предыдущий пример, добавив численное сравнение вида


“если игрок на расстоянии меньше 30 [единицы измерения расстояния] от меня, то
атаковать его”, но что мы в итоге получим? Мы всё еще проверяем соблюдение условия,
даже если оно хранится или вычисляется где-то в другом месте программы. Кроме того,
“вижу игрока” и “игрок на расстоянии <30” – всё еще очень простые функции.
Существует еще тонкая грань между двумя потенциальными состояниями “покоя” и
“атаки”, все тонкости процесса теряются.

Итак, как мы можем поступить по-другому? Ответ лежит не в существовании чисел


в нашем мире, но в том, что из себя представляют эти числа, и, в конечном счете, как мы
их используем.

Заглянем внутрь

Остановитесь на секунду и прислушайтесь к своим ощущениям. Сейчас, когда вы


читаете эту статью, чувствуете ли вы голод? Конечно, для простоты вы можете ответить
“да” или “нет”, однако, обычно здесь кроется нечто большее. Когда моя дочь была
моложе, она говорила, что “голодна”, уже после того, как немного перекусила (что
обычно означало, что она съела 2-3 кусочка и вернулась за добавкой 20 минут спустя). С
другой стороны, я легко могу сказать, что для меня состояние “голодный” означает “уже
не сытый”. Моя жена имеет склонность отвечать туманно “может быть” (что обычно не
так удобно, как звучит).

Всё это имеет значения для нас на интуитивном уровне. “Голод” — это континуум.
Мы не совершаем резкого перехода от “сыт” к “голоден”, а спускаемся постепенно. То,
что мы делаем в этой информации, может меняться в зависимости от того, в каком месте
шкалы мы находимся. Мы можем принимать решения вида “я недостаточно голоден,
чтобы пойти на кухню сейчас, я хочу закончить писать эту статью”. Мы можем делать
сравнения вида “я немного голоден, но гораздо сильнее устал”. Мы можем даже зайти так
далеко, что используем информацию о своем состоянии для предсказания будущего –
15

“Сейчас я не очень голодный, но если я не поем до начала полета, то мой живот взорвется
где-то над Вайомингом. Наверное, лучше перекусить, пока есть возможность”.

Сравните это с тем, как чаще всего написан ИИ для игровых персонажей.
Промежуточные состояния между граничными значениями для них утеряны. Солдаты
могут перезаряжаться, только когда в их оружии полностью закончились патроны, даже
если находятся в спокойном состоянии. Напарники используют большие ценные аптечки
на том, что требует максимум легкой косметики. Примеры выше легко закодировать:

если (патронов == 0)
{
перезарядка;
}

если (здоровье < 100)


{
использовать аптечку;
}

Конечно, мы могли бы изменить порог для перезарядки на “патронов <=5”, но это


принципиально не повлияет на ситуацию. Мы просто обнаружили бы агента в положении,
когда у него есть 6 патронов, и, как в названии фильма, всё тихо на западном фронте. То
есть, хотя агент ничего больше не делает, он следует четко заданному Правилу
Перезарядки и будет ждать, пока в его оружии не останется точно пять патронов.

Кроме того, может возникнуть и неприятная ситуация с агентом, который


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

Излишне говорить, что это чрезвычайно упрощенные примеры, и всё же многие из


нас сталкивались с похожим поведением в играх. Проблема вовсе не в отсутствии
информации – как мы уже обсуждали, вся необходимая информация заложена в игровом
движке. Проблема заключается в том, что разработчики ИИ не используют эту
информацию в творческом ключе, приближенном к тому, как реальные люди принимают
решения. Крайне редко мы, люди, принимаем решения, основываясь на каком-то одном
критерии. Как представители гипотетического вида Homo economicus, мы можем
сравнивать и противопоставлять входные данные из окружающего мира, делать на их
основе выводы, и, в конечном счете, принимать решения. Хитрость в том, чтобы наделить
наши ИИ творения способностью делать такие сравнения самостоятельно и без нашей
помощи.

Расчищаем поле

Итак, как нам достичь результата? Первым шагом заключается в том, чтобы сделать
данные более однородными, после чего сравнение станет не только возможным, но и
простым. Даже работая с конкретными числами, трудно согласовать разрозненные части
модели.

Рассмотрим пример агента, который может иметь максимум 138 здоровья и 30


патронов в полностью заряженном оружии. Если в конкретный момент времени у агента
16

51 очко здоровья и 23 патрона в ружье, на первый взгляд, нам не обязательно знать, какое
из 2х состояний более критично. Большинство из нас автоматически переведут эту
информацию в проценты – даже на простом уровне вида “у него меньше половины
здоровья, но больше половины патронов’. Таким образом мы пришли к первому решению
– нормализации данных.

Мои дорогие читатели наверняка знакомы с термином “нормализация”, слышали о


нем или точно знают его значение. Говоря по-простому – это пересчет данных в
процентах – в значениях от 0 до 1. В описанном выше примере здоровье нашего агента
было 0.369, а состояние патронов – 0.575. Такой метод рассмотрения данных не только
позволяет проводить прямые сравнения – т.е. 0.575> 0.369, но и значительно упрощает
обработку данных в изменяющихся условиях. Если наш агент вырос в уровне, и теперь
имеет 147 очков здоровья, мы не должны применить это изменение в нашей формуле
сравнения. Наше 51 очко здоровья теперь превратилось в 0.347 (51/147), а не 0.369, и мы
должны отделить код сравнения от любых изменений, которые мы проводим над
фактическими значениями параметров.

Но что это значит?

Нормализация, тем не менее, только устанавливает фактическое состояние дел.


Простого сравнения процентов между статусами типа “здоровье” и “патроны” обычно
недостаточно для определения относительной важности их значений. Например, я
утверждаю, что иметь 1% здоровья намного опаснее, чем иметь 1% патронов. Нужно
ввести понятие пользы.

Польза означает нечто большее, чем простой набор значений. Значения, как в нашем
нормализованном примере выше, оперируют конкретными числами. С другой стороны,
польза описывает концепцию. В нашем случае концепцией выступает “актуальность”.
Хотя она зависит от конкретных цифр здоровья и патронов, актуальность остается
обособленной.

Самый простой способ ввести понятие пользы – создание “кривой значений’.


Подумайте о прохождении реальных чисел через фильтр вида “что это конкретное
значение означает для меня?”. Именно так значения превращаются в пользу. Такой
фильтр обычно представляет собой некую формулу, через которую мы пропускаем
необработанные данные. В отличие от таблицы поиска по диапазонам (таким, как
“количество патронов >5”), у нас есть преимущество непрерывного преобразования
данных. Позже мы увидим, чем это выгодно.

Выбор формулы требует принять во внимание специфические свойства перевода


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

Первым фильтром, через который мы можем пропустить наши числа, будет простое
линейное преобразование (для этих примеров я буду использовать стандартные оси х и у).
Рассмотрим формулу:

y = 0.8x + 2
17

Результатом будет линия, проходящая от наших максимальных значений (1.0, 1.0) к


значению у = 0, когда х = 0.2 (смотрите рис.1). Другими словами, мы хотим добиться
устойчивого спуска в нашем показателе пользы (у) несколько более быстрыми темпами,
чем при фактическом значении х. Чего-то похожего можно достичь, изменив формулу на:

y = 0.8x

В этом случае, линия проходит от (1.0, 0.8) до (0, 0).

Рис. 1

Очевидно, что изменение наклона линии – в данном случае, 0.8 – должно изменить и
скорость, с которой польза меняется в зависимости от значения х. Если, например, мы
изменим его на 1.2, то скорость спуска значительно увеличится (см. рис.2).

y = 1.2x − 0.2

Стоит заметить, что эти формулы лучше всего применять в сочетании с


прижимающей функцией, которая гарантирует, что 0.0 ≤ y ≤ 1.0. Когда мы примем это во
внимание, мы получим еще одну возможность для определения пользы – когда х<0.2, у
всегда равен 0.

С другой стороны, рассмотрим похожую формулу:

y = 1.2x

Здесь мы получаем схожий эффект, за исключением того, что теперь мы приходим в


зону “нет влияния”, когда х> 0.8. То есть, показатель пользы не начнет изменяться, пока
значения коэффициента меньше, чем 0.8

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

Рис. 2

Вводим экспоненту

Простые формулы выше только подготовили почву для дальнейших манипуляций.


Например, представьте ситуацию, когда значение какого-то параметра начинается с “не
особенно важно”, но его важность растет с всё увеличивающейся скоростью. Количество
патронов в нашем оружии, показанное выше – прекрасный пример такого параметра. В
этом случае значение переменной – это всего лишь количество оставшихся выстрелов, в
то время как польза определяет актуальность перезарядки.

Анализируя ситуацию вне пределов математики – как бы мы могли повести себя –


мы можем получить подсказки, как следует подходить к математическому решению.
Представьте, что наше ружье полностью заряжено (допустим для удобства, что всего в
нем 40 выстрелов) и мы сделали одиночный выстрел. При прочих равных, мы не особо
обеспокоимся перезарядкой. С другой стороны, выстрел последнего последним патроном
будет достаточно тревожным. Ведь иметь даже 1 выстрел в запасе лучше, чем не иметь ни
одного. С такой точки зрения, будет полезным начать с этих двух конечных состояний и
двигаться к середине. Что мы будем ощущать, имея 35 патронов вместо 5? 30 вместо 10?
В конце концов, мы обнаружим, что серьезное беспокойство о перезарядке проявится,
когда наш боезапас упадет до примерно 20 патронов – после чего беспокойство будет
нарастать намного быстрее!

В простом варианте, эту ситуацию можно описать следующей формулой:

 y = (x − 1)2

По мере того, как мы тратим патроны в ружье (х), растет возможная польза от
перезарядки, но скорость ее роста также постоянно растет (см. рис.3). Это станет еще
более заметным, если мы будем менять показатель экспоненты на высокие значения, на 3
или 4. Кривая в таком случае значительно углубится. Стоит учесть, что версия формулы с
нечетными показателями потребует использования абсолютных значений функции, чтобы
избежать отрицательных значений.
19

Рис. 3

Еще одно небольшое замечание по манипуляции этими формулами. Мы можем обратить


кривизну графика вниз, изменив порядок в скобках:

y = (1 − x)2

Глядя на график (рис.4) можно заметить, что эта версия демонстрирует совершенно
другое поведение агента – получается, что с уменьшением числа патронов в обойме агент
всё меньше беспокоится об этом.

Рис. 4

Манипулируя устройством нашей функции, мы можем совершить много разных


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

Рис. 5

Мы можем указать, где мы хотим добиться максимальной пользы – она не обязана


находиться на обеих концах шкалы. Например, мы можем захотеть выразить зависимость
пользы от оптимального расстояния, основанного на нашем выборе оружия, чтобы быть
подальше от врага (см. рис. 6).

y = 2(1 − |(x − 0.3)|2)

Рис. 6

Мягкие пороги

Хотя мы можем получить много пользы из простых линейных и показательных


уравнений, один финальный класс формул может быть очень полезным. Сигмоидные
функции, в частности, логистическая функция, может быть использована для определения
“мягких пороговых значений” между определенными значениями. Фактически,
логистические функции часто используются как функции активации в нейронных сетях.
Их использование здесь, тем не менее, гораздо менее эзотерично.

Базовая логистическая функция имеет вид:

y = 1 / (1 + e-x)
21

В то время, как основание натурального логарифма, е, бросается в глаза в


знаменателе дроби, оно, на самом деле, опционально. На самом деле, мы можем
использовать аппроксимацию к 2.718 в нашем пространстве, 2,3, или любое другое число.
Фактически, изменяя значение е, мы можем добиться множества различных видов
графика в центральной части результирующей кривой. В стандартном виде график
представленной выше формулы показан на рисунке 7.

Рис. 7

Обратите внимание, что, к сожалению, границами графика не являются 0-1, как в


других наших примерах. Фактически, диапазон графика бесконечен в том смысле, что он
асимптотически приближается к паре значений – у=0 и у=1. Мы можем, однако,
использовать некоторые сдвиги графика, чтобы достичь диапазона 0-1, и, таким образом,
использовать его с нормализованными значениями х. Мы также можем изменить область
графика, в которой наблюдается порог, путем изменения того, что мы добавили к
экспоненте.

y = 1 / (1 + e–(10x – 5))

Сравнение и противопоставление

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

Сравним это с тем, что мы должны делать, когда не используем нормализованные


показатели пользы. В нашем примере голода/усталости/занятости мы должны были бы
построить модели состояния из нескольких частей, чтобы определить каждый вариант
нашего решения. Например:
22

Если ( (Голод > 5) && (Усталость < 3) && (Занятость < 7) ) то


{
Есть();
}

Если ( (Голод < 4) && (Усталость > 6) && (Занятость < 3) ) то


{
Спать();
}

Даже если приведенные выше значения нормализованы (т.е. лежат в диапазоне 0-1)
взрывная сложность простого сравнения различных возможных диапазонов и
сопоставления их с требуемым результатом очень быстро вышла бы из-под контроля. И
это только с 3 входами и 3 выходами! Преобразуя показатели в пользу, мы определяем,
что данные “значат для нас” внутри каждой кривой отклика. Теперь мы можем комфортно
себя чувствовать, зная, что прямое сравнение показателей пользы покажет, какой
параметр (или состояние) на самом деле наиболее важно для нас.

Эта система прекрасно расширяется до очень больших величин. Если мы хотим


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

Если вычисления и замеры всех этих чувств и желаний начинают громко заявлять о
себе, как в The Sims - это не случайно. The Sims – отличный (но далеко не единственный)
пример того, как сложные, основанные на пользе, функции могут использоваться для
симуляции достаточно разумных, контекстно-зависимых процессов принятия решений
агентами. Ричард Эванс много раз выступал на GDC по данной теме. Я искренне
рекомендую почитать его статьи и посмотреть его слайды по теме.

Использование этих методов, конечно же, не ограничено только жанром Sims-


подобных игр. Стратегии, в частности, требуют даже более тонких расчетов. Даже в
современных шутерах и RPG от агентов ожидают принятия всё более правдоподобных
решений в условиях, которые содержат всё больше информации. Наш ИИ больше не
может позволить себе роскошь опираться на простые логические цепочки “если я вижу
игрока, я стреляю в него!” в качестве единственного ориентира, и стандартные наборы
правил, которые касаются всех возможных состояний мира, становятся всё более
неудобными и хрупкими.

Тем не менее, я показал (хоть и очень кратко), что включение некоторых очень
простых методов позволяет нам отойти от этих сложных, часто надуманных, а иногда
даже противоречивых наборов правил. Это так же позволяет нам, дизайнерам ИИ, думать
в знакомых терминах “насколько” – таких же терминах, какие мы обычно используем,
когда думаем о своем собственном (человеческом) состоянии. Нужные нам числа уже
здесь. Магия в том, как правильно применить их.

You might also like