sztuczna inteligencja - Software Developer's Journal
SPIS TREŚCI
BIBLIOTEKA MIESIĄCA
4 Biblioteka boost::python. Łączenie C++
i Pythona.
Robert Nowak
Wykorzystywanie różnych języków programowania pozwala
dobierać właściwe narzędzie do danego problemu.
W artykule omówiono potrzebę stosowania tego typu rozwiązań
oraz pokazano przykłady łączenia C++ i Pythona
przy pomocy biblioteki boost::python.
PROGRAMOWANIE C++
8 Zastosowanie technik szablonowych
do budowania list typów. Operacje na
listach typów oraz wykorzystanie ich do
implementacji multimetod (multimethods lub
multiple dispatch).
Jarosław Bednarz
Język C++ nie posiada wsparcia dla wygodnego operowania
typami. Jedyny dostępny mechanizm to Run-Time
Type Information.
SZTUCZNA INTELIGENCJA
12 W oczekiwaniu na sztuczną inteligencję.
Dr Adrian Horzyk
Inteligencja jest zjawiskiem powszechnym i mimo wielu
krążących na jej temat opinii i definicji, pewne jest, że pozwoliła
i pozwala się naszej cywilizacji rozwijać. Bez wątpienia
poziom inteligentnych zachowań podwyższa się
z biegiem czasu, co można u ludzi obserwować w coraz
bardziej rozwiniętym i skomplikowanym sposobie zachowania
się, myślenia i kojarzenia.
18 Dlaczego maszyna potrafi zachować się
jak istota inteligentna? „Wirtualny doradca”
- krok po kroku.
Dr Agnieszka Nowak – Brzezińska
Zakładając, że odbiorcami niniejszego artykułu są fascynaci
sztuczną inteligencją z pewnością nie trzeba długo
argumentować tytułu pracy. Sztuczna inteligencja jako
nauka obejmująca zagadnienie logiki rozmytej, obliczeń
Miesięcznik Software Developer’s Journal
(12 numerów w roku)
jest wydawany przez
Software Press Sp. z o.o. SK
Prezes: Paweł Marciniak
Dyrektor wydawniczy: Natalia Sieniutowicz
Redaktor naczelny: Łukasz Łopuszański
lukasz.lopuszanski@software.com.pl
Skład i łamanie: Tomasz Kostro
www.studiopoligraficzne.com
Kierownik produkcji: Andrzej Kuca
andrzej.kuca@software.com.pl
Adres korespondencyjny:
Software Press Sp. z o.o. SK,
ul. Bokserska 1, 02-682 Warszawa, Polska
tel. +48 22 427 36 91, fax +48 22 224 24 59
www.sdjournal.org
cooperation@software.com.pl
Dział reklamy: adv@software.com.pl
Redakcja dokłada wszelkich starań, by
publikowane w piśmie i na towarzyszących mu
nośnikach informacje i programy były poprawne,
jednakże nie bierze odpowiedzialności za
efekty wykorzystania ich; nie gwarantuje także
4/2011 (196)
ewolucyjnych, sieci neuronowych, sztucznego życia i robotyki
jest działem informatyki badającym reguły rządzące
inteligentnymi zachowaniami człowieka, tworzącym
modele formalne tych zachowań i - w rezultacie – programów
komputerowych symulujących te zachowania.
28 Analiza możliwości wykorzystania
sztucznych sieci neuronowych w psychologii.
Wprowadzenie do sieci neuronowych,
rozpoznawanie płci w środowisku pakietu
sztucznej inteligencji Sphinx.
Dr Inż. Paweł Piotrowski
Sztuczne sieci neuronowe jako jedna z kilku technik zaliczanych
do kategorii „sztuczna inteligencja” mogą być
z dużym powodzeniem wykorzystywane również w takich
obszarach nauki jak psychologia. Sposób działania (uczenie
się) sztucznych sieci neuronowych naśladujący nieco
sposób uczenia się człowieka sprawia, że zadania z gatunku
podziału zbiorów na kategorie (klasy) nie stanowią
dla tego narzędzia żadnych problemów, a efekty nauki są
doskonałe.
PROGRAMOWANIE GRAFIKI
36 OpenGL i komponenty Windows
Forms. Renderowanie sceny za pomocą
funkcji OpenGL w aplikacji korzystającej
z komponentów Windows Form.
Korneliusz Warszawski
Projektując aplikacje korzystające z biblioteki graficznej
OpenGL samodzielnie musimy zadbać o prawidłowe rozmieszczenie
i oprogramowanie interfejsu użytkownika.
Przy jego projektowaniu idealnie wspomoże nas wykorzystanie
środowiska Microsoft Visual Studio, które odciąży
nas od czasochłonnego zajęcia. W artykule tym znajdziesz
podstawowe informację na temat tego, jak w prosty
sposób połączyć funkcję biblioteki OpenGL z aplikacją
korzystającą z komponentów Windows Forms.
50 Stereoskopia. Implementacja w XNA 4.0.
Jacek Matulewski, Wojciech Stańczyk
Stereoskopia na dobre zadomowiła się w kinach. Niemal
wszystkie przeboje, począwszy od „Avatara” po filmy
animowane dla dzieci są teraz wyświetlane w wersji 3D.
Także wśród urządzeń kina domowego pojawiają się już
urządzenia do prezentacji obrazu z wrażeniem głębi. Mowa
zarówno o telewizorach, jak i o akcesoriach komputerowych.
poprawnego działania programów shareware,
freeware i public domain.
Wszystkie znaki firmowe zawarte w piśmie są
własności odpowiednich firm.
Zostały użyte wyłącznie w celach informacyjnych.
Osoby zainteresowane współpracą
prosimy o kontakt:
cooperation@software.com.pl
Reklama
SPIS TREŚCI
TESTOWANIE
OPROGRAMOWANIA
60 Czy zauważyłeś goryla? Praktyczne
umiejętności dostrzegania błędów aplikacji.
Bogdan Bereza - Jarociński
Jakość jest ważna, zgoda? Nie tylko jako dobro luksusowe,
ale przede wszystkim jako sposób na podniesienie
wydajności i na to, aby budowanie aplikacji było łatwiejsze,
skuteczniejsze i przyjemniejsze. Wiele jest sposobów
zapewnienia jakości, niektóre bardzo wyrafinowane, ale
nic nie zastąpi podstawowej umiejętności SPRAWDZA-
NIA, czy to, co zrobiono, zrobiono dobrze. Sprawdzać muszą
programiści, projektanci, analitycy i użytkownicy, no
i oczywiście zawodowi sprawdzacze - testerzy. Na każdym
poziomie, podczas każdego kroku wytwarzania.
60 Kariera w testach oprogramowania.
Jacek Tomczak
Artykuł kierowany jest do osób, które są na etapie wyboru
odpowiedniej dla siebie ścieżki rozwoju w branży IT lub zastanawiają
się nad zmianą dotychczasowej drogi zawodowej.
Chciałbym im przybliżyć obszar testów, główne zadania
testera, miejsce zespołu testów w organizacji. Pokażę,
co ciekawego w tym zespole można znaleźć dla siebie.
WYWIADY
SPIS TREŚCI
68 O automatyzacji usług IT i realiach cloud
computing.
Rozmowa z Gurem Steifem, gościem BMC Forum w Warszawie
72 Oczekiwania zależą od projektu.
Wywiad z Dominiką Kotułą, menagerem personalnym
w firmie Blue Media S.A.
4
BIBLIOTEKA MIESIĄCA
Łączenie C++ i Pythona
biblioteka boost::python
Wykorzystywanie różnych języków programowania
pozwala dobierać właściwe narzędzie do danego problemu.
W artykule omówiono potrzebę stosowania tego typu
rozwiązań oraz pokazano przykłady łączenia C++ i Pythona
przy pomocy biblioteki boost::python.
Dowiesz się:
• Dlaczego warto używać kilku języków programowania;
• Jak wołać kod C++ z Pythona;
• Jak wołać kod Pythona z C++.
Wstęp
Nie istnieje jeden, najlepszy język programowania.
Przy projektowaniu języka dokonuje się pewnych wyborów,
które zwiększają korzyści ze stosowania danego
języka w pewnych obszarach, ale sprawiają kłopoty
w innych - nietypowych dla danego języka zastosowaniach.
Wśród tysiąca obecnie istniejących języków
programowania trudno wybrać jeden najlepszy, dlatego
decyzja, którego języka użyć powinna być zależna
od wymagań stawianych przed tworzoną aplikacją
oraz indywidualnych preferencji programistów.
Stosowanie zawsze jednego języka prowadzi do
tworzenia niezbyt wyrafinowanych rozwiązań. Warto
zainwestować czas na poznanie przynajmniej kilku
różnych języków, oprócz korzyści ze stosowania
najbardziej odpowiedniego narzędzia do danego zastosowania,
pozwala to także dostrzec pewne ogólne
techniki stosowane w programowaniu.
Jedną z cech specyficznych dla danego języka programowania,
jest metoda przekształcania kodu źródłowego
w działający program. Języki kompilowane
translują kod źródłowy, tworzony przez programistę,
na kod wynikowy (binarny), następnie kod wynikowy
jest wykonywany przez komputer. Inaczej proces
ten rozwiązują języki interpretowane, czytają one fragment
kodu źródłowego i wykonują ten fragment. Tutaj
kod wynikowy (kod binarny) nigdy nie powstaje.
Języki kompilowane pozwalają nam tworzyć bardzo
wydajne fragmenty kodu - wykonuje się kod binarny,
Powinieneś wiedzieć:
• Jak pisać proste programy w C++;
• Jak pisać proste programy w Pythonie.
ale proces implementacji jest trudniejszy, niż przy użyciu
języków interpretowanych. Kod jest ukryty przed
użytkownikiem, więc możemy chronić w ten sposób
nasze rozwiązania. Aplikacje nie są przenośne, kod
binarny jest zależny od platformy. Programy napisane
w językach interpretowanych wykonują się wolniej,
ale proces implementacji jest łatwiejszy. Programy są
przenośne, zaś kod jest dostępny dla użytkownika. Istnieją
języki pośrednie, mające cechy obu wymienionych
klas, ale ich stosowanie sprawia, że godzimy się
na pewne koszty, na przykład wydajność takich aplikacji
nigdy nie będzie taka, jak wydajność aplikacji stworzonych
w językach kompilowanych.
Szybki start
Aby uruchomić przedstawione przykłady, należy mieć dostęp
do kompilatora C++ oraz edytora tekstu. Przykłady wykorzystują
biblioteki boost (www.boost.org). Aby poprawnie
je skompilować należy dodać odpowiednie zależności wykorzystywane
podczas konsolidacji; dla konsolidatora g++
należy dodać opcje: -lboost _ python; dla konsolidatora
Visual Studio (program link) biblioteki boost są dodawane
automatycznie. Przykłady wykorzystują także interpreter
języka Python, który także powinien być zainstalowany
(patrz www.python.org). Na wydrukach pominięto dołączanie
odpowiednich nagłówków oraz udostępnianie przestrzeni
nazw, pełne źródła umieszczono jako materiały pomocnicze.
Materiały te wykorzystują narzędzie do budowania
aplikacji Scons (www.scons.org).
4/2011
Biorąc pod uwagę tylko jedną cechę, czy język jest
kompilowany, czy interpretowany, można wskazać
fragmenty aplikacji, które wygodniej tworzyć za pomocą
języków kompilowanych, oraz fragmenty, które
wygodniej implementować przy pomocy języków
interpretowanych. Aplikacja posiada pewne fragmenty,
które powinny być wykonywane wydajnie. Są one
„wąskim gardłem” i aby osiągnąć maksymalną wydajność
musimy je tworzyć w językach kompilowanych.
Pozostałe fragmenty powinny być utworzone możliwie
szybko, a szybkie tworzenie programów umożliwiają
języki interpretowane. Innym powodem stosowania
języków interpretowanych jest umożliwienie użytkownikowi
modyfikacji (czy dostosowywania) aplikacji do
własnych potrzeb; jeżeli pewne fragmenty (np. mapa
gry) są programem wykonywanym przez interpreter, to
użytkownik może je łatwo zmienić.
Tworzenie aplikacji wykorzystującej różne języki
programowania wymaga, oprócz umiejętności programowania
w tych językach, zapewnienia komunikacji
pomiędzy modułami. Wymusza to dokładne określenie
interfejsu odpowiednich modułów oraz utworzenie
procedur konwertujących obiekty oraz sposoby
wołania pomiędzy fragmentami napisanymi w różnych
językach.
Tematem tego artykułu jest biblioteka boost::python.
Biblioteka ta „obudowuje” interfejs do interpretera (Python
C API) pozwalając na wygodne łączenie kodu
napisanego w C++ (C++ to język kompilowany) i kodu
napisanego w Pythonie (Python jest interpreterem).
Biblioteka boost::python
Wołanie funkcji i metod zaimplementowanych w C++
z języka Python, czyli rozszerzanie Pythona w C++,
pozwala na efektywną implementację tych fragmentów,
które powinny być wydajne. Innym powodem stosowania
,,wstawek'' w C++ jest konieczność ukrycia
kodu przed użytkownikiem.
Biblioteka boost::python dostarcza zbioru szablonów,
które pozwalają utworzyć interfejs do funkcji
i klas utworzonych w C++. Tak obudowane klasy
i funkcje będą dostępne dla programów tworzonych
w języku Python. Kod utworzony w C++ jest kompilowany
do biblioteki dzielonej, zawierającej odpowiedni
interfejs. Przykład pokazano na Listingu 1. Dostarczamy
tam funkcję f oraz klasę Foo.
Definicję interfejsu rozpoczynamy podając nazwę pakietu
Pythona, jest to argument makrodefinicji BOOST_
PYTHON_MODULE. Nazwa ta powinna być taka sama, jak
nazwa biblioteki dzielonej, która zawiera udostępniane
byty. Makrodefinicja generuje m.in. funkcję wymaganą
przez Python przy ładowaniu pakietu (funkcja void
initnazwa(void)). Wewnątrz bloku, który jest umieszczony
za makrodefinicją BOOST_PYTHON_MODULE, umieszczamy
deklaracje funkcji i klas. Funkcję eksportujemy
Łączenie C++ i Pythona
za pomocą szablonu def, podając nazwę funkcji, która
będzie używana w Pythonie oraz wskaźnik na funkcję
w C++. Klasę eksportujemy za pomocą szablonu
class_. Parametrem tego szablonu jest typ w C++, natomiast
argumentami nazwa typu, która będzie używana
w Pythonie oraz argumenty konstruktora (w Pytonie
rolę konstruktora pełni metoda __init__). Dodatkowo
podajemy nazwy metod używane w Pythonie wraz
ze wskaźnikiem na metodę w C++.
Po utworzeniu biblioteki dynamicznej (dla przykładu
z Listingu 1 bibliotekaaa ta powinna mieć nazwę
cppmodule.so dla Linuxa, cppmodule.pyd dla Windows)
i umieszczeniu jej w miejscu, które jest widoczne dla
interpretera Pythona (np. w katalogu z którego interpreter
został uruchomiony) możemy wykonać następujące
instrukcje:
>>> import cppmodule
>>> cppmodule.f(3)
www.sdjournal.org 5
45
>>> foo = cppmodule.Foo(2)
>>> foo.get()
2
>>> foo.set(5)
>>> foo.get()
5
Pierwsza linia dołącza pakiet o danej nazwie (w tym
wypadku jest to moduł, który stworzyliśmy w C++),
kolejne wołają funkcję f, tworzą obiekt klasy Foo oraz
wołają dla niego metody.
Obecnie często korzysta się z dodatkowych narzędzi,
które generują kod eksportujący klasy i funkcje;
autor wykorzystuje bibliotekę pyplusplus (http://
sourceforge.net/projects/pygccxml/). Biblioteka ta generuje
kod w C++ na podstawie dostarczonych plików
zawierających definicje. Kod ten definiuje moduł (makrodefinicja
BOOST_PYTHON_MODULE, patrz Listing 1) oraz
zawiera odpowiednie wpisy dla funkcji i klas. Narzędzia
te zmniejszają nakład pracy potrzebny przy łączeniu
modułów tworzonych w różnych językach.
Python
Python jest interpretowanym, interaktywnym językiem programowania
stworzonym przez Guino van Rossuma w 1990,
obecnie rozwijanym przez Python Software Fundation. Język
ten wspiera programowanie obiektowe, strukturalne
i funkcyjne. Ze względu na bogatą bibliotekę standardową,
prostotę, przejrzystość kodu źródłowego (np. bloki są zaznaczane
przez wcięcia), przenośność, brak statycznej kontroli
typów, możliwość umieszczania dokumentacji w kodzie
źródłowym staje się on coraz bardziej popularny – działającą
aplikację można utworzyć w bardzo krótkim czasie.
Język Python został ciekawie opisany w książce „Zanurkuj
w Pythonie” dostępnej na wikibooks (www.wikibooks.org)
6
Przekazywanie typów złożonych
Biblioteka boost::python pozwala przekazać do C++
nie tylko typ prosty, ale także obiekt klasy utworzonej
w Pythonie, a nawet kolekcję takich obiektów. Dostarczana
jest klasa object, reprezentująca obiekt zarządzany
przez interpreter Pythona. Klasa object dostarcza
wielu metod, które posiada zmienna w Pythonie,
m.in. metodę umożliwiającą odczyt składowych.
BIBLIOTEKA MIESIĄCA
Listing 1. Przykład wykorzystania urządzenia odmierzającego czas
//przykładowa funkcja, która będzie wołana przez Python
int f(int a) { return a + 42; }
class Foo { //przykładowa klasa
public:
Foo(int v) : val_(v) {}
int get() const { return val_; }
void set(int v) { val_ = v; }
private:
};
int val_;
BOOST_PYTHON_MODULE( cppmodule ) //makrodefinicja tworząca pakiet
{
boost::python::def( "f", f );//eksportuje funkcję do Pythona
// eksportuje klasę do Pythona
boost::python::class_("Foo", boost::python::init())
.def( "get", &Foo::get )
.def( "set", &Foo::set )
;
} //koniec definicji pakietu, który będzie dostępny w Pythonie
Listing 2. Dostęp do składowych obiektu Pythona
ptime getDateTime(const object& d) {
}
return ptime( date(extract(d.attr("year") ),
);
extract(d.attr("month") ),
extract(d.attr("day") ) ),
time_duration( extract(d.attr("hour") ),
Listing 3. Tworzenie obiektu w C++ i zwracanie go do Pythona
//tworzy listę używaną przez program w Pythonie
boost::python::list convertFromVect(const std::vector& v) {
}
boost::python::list out;
Przykład funkcji, która konwertuje zmienną Pythona
zawierającą punkt w czasie, tzn. obiekt typu
datetime.datetime na obiekt dostarczany przez boost:
:date_time został pokazany na Listingu 2. Funkcja
getDateTime odczytuje poszczególne składowe dostarczonego
obiektu. Dostęp do składowej daje metoda
attr, natomiast konwersja na wybrany typ w C++ jest
wykonywana przez szablon extract. Funkcja odczy-
extract(d.attr("minute") ),
extract(d.attr("second") ) )
for(std::vector::const_iterator i = v.begin(); i != v.end(); ++i)
out.append( *i );
return out;
4/2011
Listing 4. Program w C++, który woła interpreter Pythona
int main() {
}
Py_Initialize();//inicjuje interpreter
dict global; //słownik obiektów globalnych
tuje składowe year, month, itd., przekształca je na typ
całkowity (int), a następnie tworzy odpowiedni obiekt
z biblioteki boost::date_time.
Biblioteka boost::python dostarcza klas pochodnych
po object, m.in. dict – reprezentuje słownik
z Pythona, list – reprezentuje listę. Dla wspomnianych
kontenerów istnieje przeciążony operator indeksowania
oraz metoda len (zwraca ilość elementów).
Obiekty tworzone w C++ i zwracane do Pythona
przez wartość są zarządzane przez Pythona i prawidłowo
usuwane (przez ,,odśmiecacza'', który jest częścią
interpretera), więc w prostych przypadkach nie
musimy się martwić o czas życia obiektów. Przykładem
wykorzystania tego mechanizmu jest lista Pythona
zwracana przez funkcję convertFromVect pokazaną
na Listingu 3.
Osadzenie Pythona w C++
W pewnych sytuacjach chcemy dostarczyć fragment
kodu w języku interpretowanym, aby użytkownik mógł
zmieniać ten fragment bez potrzeby przebudowywania
aplikacji. Taka sytuacja występuje często w aplikacjach,
które są w pewnym stopniu dopasowywane do
konkretnego zastosowania (do potrzeb konkretnego
klienta). Aby nie tworzyć własnego interpretera można
wykorzystać Python.
Łączenie C++ i Pythona
object result = exec("value = 43\n", global, global); //wykonuje napis przez interpreter Pythona
std::cout
8
PROGRAMOWANIE C++
Zastosowanie technik
szablonowych do
budowania list typów
Operacje na listach typów oraz wykorzystanie ich do
implementacji multimetod (multimethods lub multiple
dispatch)
Język C++ nie posiada wsparcia dla wygodnego operowania typami.
Jedyny dostępny mechanizm to Run-Time Type Information.
Dowiesz się:
• Jak używać zaawansowanych technik szablonowych do manipulowania
typami w C++
• Jak zbudować prosty mechanizm implementujący multimetody
w C++
Dostarcza on podstawowych funkcji do identyfikowania
typów w trakcie działania programu,
jednak nie pozwala na łatwe operowanie typami
i jego użyteczność ogranicza się raczej do celów
diagnostycznych. Listy typów, o których mowa w tym
artykule opierają się na mechanizmach szablonowych
i wykorzystują techniki używane w tzw. meta programowaniu
w C++ opartym o szablony. Metaprogramowanie
jest mechanizmem działającym podczas kompilacji
programu (ang. compile time), a nie w czasie jego
wykonywania (ang. run time), dlatego nie wprowadza
żadnych obciążeń mających wpływ na wydajność programu.
Cała praca wykonywana jest przez kompilator
w czasie kompilacji programu. Oczywiście nie wszystkie
zaprezentowane w tym artykule mechanizmy wykorzystują
w 100% metaprogramowanie, część operacji
wykonywana jest również w runntime, jednak ich
wpływ na wydajność programu jest niski.
Tak jak już wspomniano powyżej, implementacja
list typów oparta jest o techniki szablonowe, a dokładnie
o mechanizm częściowej specjalizacji szablonów.
Podstawowym elementem używanym do budowania
list typów jest prosta struktura zawierająca jedynie definicje
dwóch typów zapisanych w deklaracjach typów
Head i Tail.
template
Powinieneś wiedzieć:
• Dobra znajomość programowania szablonów w C++
• Specjalizacja częściowa szablonów w C++
struct CTypeList
{
public:
};
typedef THead Head;
typedef TTail Tail;
Należy zauważyć, że powyższa struktura nie przechowuje
żadnych danych. Jest ona tylko opakowaniem
przechowującym definicje dwóch typów
i w czasie działania programu również nie niesie ze
sobą żadnych danych. Podobnie jak w typowej implementacji
elementu listy zawierającej dane, struktura
zawiera elementy służące do wiązania jej z innymi
strukturami. W typowej liście jednak wiązanie
odbywa się przez referencję lub wskaźnik. W listach
typów wiązanie odbywa się poprzez zagnieżdżanie
definicji typów w polach Tail. I tak lista zawierająca
dwa typy np. char i long będzie wyglądać następująco:
CTypeList
Lista zawierająca trzy typy np. char, long i int będzie
wyglądać już tak:
CTypeList
4/2011
Zastosowanie technik szablonowych do budowania list typów
W ten sposób możemy dowolnie zagnieżdżać elementy
CTypeList tworząc dowolnie długie konstrukcje. W powyższych
definicjach jest jednak jeden problem, nie da
się za ich pomocą zapisać list zawierających np. jeden
element. Nie mają też elementu kończącego, analogicznego
do zera kończącego łańcuchy znakowe w C/C++.
Funkcjonalność zera kończącego można osiągnąć poprzez
zdefiniowanie następującego typu:
struct CZero
{
};
Dzięki zdefiniowaniu struktury CZero, możemy teraz zdefiniować
listę zawierającą tylko jeden typ oraz zakończyć
listy zawierające więcej typów jednym specjalnym
elementem analogicznie do łańcuchów znakowych:
CTypeList
oraz
CTypeList
Tworzenie długich list typów w sposób opisany powyżej
może być kłopotliwe już dla kilku typów. Tutaj z pomocą
przychodzą nam jednak makra preprocesora. Ponieważ
listy typów są strukturami rekurencyjnymi, to stosunkowo
łatwo można napisać dowolną ilość makr definiujących
listy o podanym rozmiarze (Listing 1).
Mając tak zdefiniowane makra można już definiować
listy typów w bardzo wygodny sposób, na przykład de-
Listing 1.
#define TL1(T1) CTypeList
#define TL2(T1,T2) CTypeList
#define TL3(T1,T2,T3) CTypeList
#define TL4(T1,T2,T3, T4) CTypeList
template struct CTypeListLength
{
};
enum {Value = 1 + CTypeListLength::Value };
template struct CTypeListLength
{
};
enum { Value = 0};
klaracja listy czteroelementowej będzie wyglądać następująco.
typedef TL4(int, char, long, bool) TMyList;
Domknięcie listy specjalnym elementem typu „zero” pozwala
nam już policzyć jej długość. Aby policzyć długość
listy typów należy użyć częściowej specjalizacji
szablonów (Listing 2).
Jak działa powyższy kod? Otóż ogólny algorytm liczenia
długości działa rekurencyjnie. Zakłada, że długość listy
jest sumą długości podlisty zawartej w polu Tail plus
1. Oczywiście podlista zawarta w polu Tail może również
zawierać podlisty, te z kolei następne podlisty i tak da-
Listing 3
class CVehicle
www.sdjournal.org 9
{
public:
};
CVehicle(){};
virtual ~CVehicle(){};
class CCar : public CVehicle
{
public:
};
void CheckEngine();
void ChangeOil();
class CBike : public CVehicle
{
public:
};
void CheckWheels();
Listing 4.
class CWorkshop
{
public:
};
void operator()( CCar* pCar )
{
}
pCar->RepairEngine();
pCar->ChangeOil();
void operator()( CBike* pBike )
{
}
pBike->CheckWheels();
10
lej aż do listy, która w polu Tail zawiera typ CZero.
Dla CZero długość wynosi 0, dla tego typu algorytm
kończy również przetwarzanie rekurencyjne.
I tak dla zdeklarowanych dwóch typów:
typedef CTypeList TListShort;
typedef CTypeList
TListLong;
Używając wyżej napisanych funkcji obliczających
długość otrzymamy w zmiennych i1 oraz
i2 wartości 1 oraz 2:
int i1 = CTypeListLength::Value;
int i2 = CTypeListLength::Value;
Typowym zastosowaniem list typów w C++
są multimetody (ang. Multimethods lub multiple
dispatch). Technika ta na razie niedostępna
w C++ pozwala na wywoływanie różnych przeciążeń
jednej funkcji bazując na aktualnym typie
wskaźnika do obiektu polimorficznego. Załóżmy,
że mamy klasę bazową CVehicle, oraz dwie klasy
pochodne CCar i CBike (Listing 3).
Wskaźniki na obiekty tych klas przekazywane
są do jednej instancji klasy odpowiedzialnej
za ich przetwarzanie. Celem takiego podejścia
jest skoncentrowanie kodu implementującego
funkcjonalność związaną z klasami CCar i CBike
w jednym miejscu. W tym przypadku jest zakładamy,
że jest to funkcjonalności polegająca
na serwisowaniu samochodu lub roweru. Koncentracja
kodu oraz rozdzielenie go w zależności
od typu przekazywanego obiektu jest w tym przypadku
o tyle ważne, że implementacja serwisowania
w przypadku samochodu wymaga wywołania
zupełnie innego zestawu funkcji niż w przypadku
serwisowania roweru. Załóżmy, że kod
implementujący serwisowanie zostanie napisany
w następującej klasie CWorkshop, posiadającej
dwa przeciążenia operatora (), jedno dla samochodu
i jedno dla roweru (Listing 4).
W klasycznym podejściu moglibyśmy zaimplementować
kod wywołujący odpowiednią metodę
klasy CWorkshop w następujący sposób (Listing
5).
W tym podejściu tzw. Dispatcher odpowiedzialny
za wywołanie odpowiedniego operatora z klasy
CWorkshop w zależności od typu przekazywanego
wskaźnika na obiekt polimorficzny, po prostu
sprawdza używając operatora dynamic_cast
i tak zwanego rzutowania „do góry” jakiego typu
jest przekazany wskaźnik. Następnie dla każdego
z typów wołane jest odpowiednie przeciążenie
operatora () w klasie CWorkshop. Zaprezentowa-
PROGRAMOWANIE C++
Listing 5.
class CClassicDispatcher
{
};
void Proceed( CWorkshop& workshop, CVehicle* pVehicle )
{
}
if( CCar* pCar = dynamic_cast(pVehicle))
{
}
workshop(pCar);
else if(CBike* pBike = dynamic_cast(pVehicle))
{
}
Listing 6.
workshop(pBike);
template < typename TypeList,
typename TBaseType,
typename TExecutor >
class CStaticDispatcher
{
public:
};
typedef typename TypeList::Head TCurrentType;
static void Execute( TBaseType* p, TExecutor executor )
{
}
TCurrentType* pCurrentItem =
if( pCurrentItem )
{
}
else
{
}
dynamic_cast(p);
executor(pCurrentItem);
CStaticDispatcher
TExecutor>::Execute(p, executor);
class CStaticDispatcher
{
public:
};
static void Execute( TBaseType* p, TExecutor executor )
{
};
4/2011
Zastosowanie technik szablonowych do budowania list typów
ne podejście jest jak najbar-
Listing 7
dziej poprawne, ma jednak
kilka wad. Przede wszyst- typedef TL2(CCar, CBike) TVehicles;
kim nie jest odporne na błę- CVehicle* pVehicle1 = new CCar();
dy. Pominięcie jednego wa- CVehicle* pVehicle2 = new CBike();
runku if spowoduje, że da- CWorkshop workshop;
na klasa w ogóle nie będzie
obsługiwana. Kod jest również
dosyć ciężki w utrzymaniu,
gdyż dodanie obsługi
do każdej następnej klasy wymaga dopisania dodatkowego
warunku if. Poniżej przedstawiono klasę szablonową,
która automatyzuje cały proces rzutowania oraz
wywoływania odpowiedniego przeciążenia operatora ()
z klasy CWorkshop.
Aby użyć powyższego szablonu należy wywołać jego
statyczną metodę Execute podając jako argumentu
wskaźnik do obiektu klasy bazowej oraz obiekt egzekutora,
dla którego będą wywoływane operatory () (w naszym
przypadku jest to klasa CWorkshop). Najważniejszym parametrem
szablonu jest TypeList, czyli lista typów zawierająca
wszystkie typy klas pochodnych dziedziczących po
TBaseType. Dodatkowo szablon wymaga podania jeszcze
typu egzekutora implementującego operatory ().
Jak działa powyższy szablon? Znowu rekurencyjnie.
Dla każdego elementu Head listy typów wykonuje rzutowanie
przekazanego jako argument funkcji Execute
wskaźnika p, sprawdzając czy wskaźnik jest typu wskazywanego
przez pole Head z listy typów. Jeśli wskaźnik
p jest typu Head, to wywoływany jest odpowiedni operator
() z klasy egzekutora (klasa CWorkshop). W przeciwnym
razie badane są następne typy z listy typów poprzez
ponowne wywołanie funkcji Execute, ale tym razem
już z krótszą listą typów, pozbawioną jednego poziomu
zagnieżdżenia zawierającego właśnie zbadany
typ zawarty w polu Head. Pamiętajmy, że listy typów to
struktury rekurencyjne, dlatego do pobrania podlisty wystarczy
nam odwołanie się do pola Tail, co wykonywane
jest we fragmencie kodu tworzącym nową instancję szablonu
CStaticDispatcher:
CStaticDispatcher::
Execute(p, executor);
Mając tak napisaną klasę dispatchera rozdzielającego
wywołania odpowiednich operatorów () na podstawie
typu przekazanego argumentu możemy w następujący
sposób zakodować serwisowanie samochodu i roweru
(Listing 7).
Efektem działania powyższego kodu będzie wywołanie
operatorów () z klasy CWorkshop w następującej kolejności:
void CWorkshop::operator()( CCar* pCar )
oid CWorkshop::operator()( CBike* pBike )
CStaticDispatcher::Execute(pVehicle1, workshop );
CStaticDispatcher::Execute(pVehicle2, workshop );
Pierwsze wywołanie funkcji Execute klasy
CStaticDispatch spowoduje wywołanie przeciążenia
operatora () klasy CWorkshop z parametrem CCar*, drugie
wywołanie funkcji Execute spowoduje wywołanie
przeciążenia operatora () z paramterem CBike*.
W ten sposób skonstruowaliśmy prosty i łatwy w użyciu
mechanizm tzw. multimetod, czyli wywoływania w trakcie
działania programu różnych przeciążeń tej samej funkcji
w zależności od faktycznego typu wskaźnika polimorficznego.
Mechanizm jest zarówno łatwy jak i prosty w utrzymaniu.
Jeśli w naszym systemie pojawią się nowe klasy
np. CMotorbike, wtedy wystarczy rozszerzyć definicję typu
TVehicles następująco:
typedef TL3(CCar, CBike, CMotorbike) TVehicles;
Klasę CWorkshop należy natomiast uzupełnić o przeciążenie
operatora ():
void operator()( CMotorbike* pBike )
Zaprezentowane techniki, to dopiero niewielki przykład
możliwości wykorzystania szablonów w C++. Wprowadzenie
wsparcia dla szablonów stworzyło wielkie możliwości
dla programistów i przeniosło język C++ na zupełnie
nowy poziom. Odpowiedniki mechanizmów szablonowych
znanych od lat w bibliotekach takich jak „boost
C++” dopiero niedawno zostały wprowadzone lub są
wprowadzane w innych językach np. C#. Mam nadzieję,
że niniejszy artykuł skłoni czytelników do zgłębiania tajemnic
szablonów i metaprogramowania w C++, bo jest
to jeden z bogatszych języków, który nawet dla doświadczonego
programisty może stanowić wyzwanie.
JAROSŁAW BEDNARZ
Jest absolwentem Politechniki Wrocławskiej. W chwili obecnej
jest dyrektorem technicznym w �rmie CodeTwo, gdzie specjalizuje
się w tworzeniu oprogramowania w C++ i .NET.
Flagowe produkty CodeTwo otrzymały najwyższe noty na opiniotwórczych
portalach traktujących o oprogramowaniu -
m.in. CNET, MSExchange.org. W ciągu 3 lat CodeTwo stało się
rozpoznawalną marką tworzącą programy na platformy Microsoft
Exchange oraz Microsoft Outlook na świecie.
Firma wciąż się rozwija i poszukuje
nowych programistów - www.codetwo.pl
www.sdjournal.org 11
12
SZTUCZNA INTELIGENCJA
W oczekiwaniu na
sztuczną inteligencję
Inteligencja jest zjawiskiem powszechnym i mimo wielu
krążących na jej temat opinii i definicji, pewne jest, że
pozwoliła i pozwala się naszej cywilizacji rozwijać.
Bez wątpienia poziom inteligentnych zachowań podwyższa
się z biegiem czasu, co można u ludzi obserwować
w coraz bardziej rozwiniętym i skomplikowanym sposobie
zachowania się, myślenia i kojarzenia.
Inteligencja pozwoliła ludzkości osiągnąć nie jeden
ważny cel i przekroczyć nie jedną granicę poznania,
zrozumienia i możliwości. Niezbędnym elementem
rozwoju inteligencji jest wiedza, która dzięki rozwojowi
technologii i szybkiemu dostępowi do informacji, również
szybciej się rozwija. W związku z coraz szybszym
rozwojem technologii ludzkość oczekuje, a wręcz wymaga,
żeby technika – w tym przede wszystkim komputery
i automaty – również postępowały możliwie inteligentnie,
a przynajmniej przejawiały pewne zdolności,
które są charakterystyczne dla inteligentnych jednostek.
Stopień skomplikowania dzisiejszych systemów
informatycznych, automatyka przemysłowa wręcz wymaga,
żeby komputery i maszyny umiały dostosować
się do otaczających je lub ich środowisko pracy warunków,
żeby umiały rozpoznawać istotne informacje i wykorzystać
je w swoim procesie obliczeniowym do osiągnięcia
możliwie optymalnych parametrów pracy lub
osiąganych wyników. Ze względu na coraz większy
stopień skomplikowania zadań, konieczność kooperacji
coraz większej ilości ludzi i systemów informatycznych,
popyt na inteligencję naturalną i sztuczną jest coraz
większy i ciągle nienasycony. Wydaje się więc, iż
w naturalny sposób nasza cywilizacja potrzebuje rozwinąć
i kooperować z inteligencją sztuczną, która mogłaby
część ważnych zadań pomóc rozwiązać. Pod presją
potrzeb ale również z ciekawości i chęci poznawczych
naukowcy na całym świecie od dziesiątek lat próbują
zbadać i dociec podstaw działania naszej ludzkiej
inteligencji. Liczne badania prowadzone są w celu odpowiedzenia
na istotne pytania związane ze zrozumieniem
naszej własnej inteligencji po to, żeby móc określić
i zdefiniować algorytmy jej działania, które pozwolą
zbudować systemy sztuczne zdolne do inteligentnych
zachowań, kojarzenia, analizy i wnioskowania. Odkrycie
tych mechanizmów oraz odpowiedź na te fascynujące
pytania może okazać się być najbardziej przełomowym
momentem dla dalszego rozwoju naszej cywilizacji.
W oczekiwaniu na sztuczną inteligencję zastanówmy
się przez chwilę, czym jest owa inteligencja,
od czego zależy, z czym jest związana, czego wymaga,
a czego my wymagamy od niej, z czym jest powiązana
i gdzie drzemie jej wykorzystany i niewykorzystany
jeszcze potencjał oraz jak możemy sprawić, żeby
w końcu zacząć eksploatować jej możliwości w sposób,
który nie zagrażałby ludzkości.
W dzisiejszych czasach pojęcie „sztucznej inteligencji”
jest często wykorzystywane w marketingu i w związku
z tym jest często nadużywane do określania algorytmów,
które przejawiają pewne zdefiniowane przez człowieka
inteligentne zachowania lub są zdolne generować
inteligentne wnioski w wyniku przetwarzania ograniczonych
zbiorów informacji. Czy jednak sztuczną inteligencją
możemy nazwać algorytmy napisane przez inteligentnego
człowieka, które są zdolne jedynie odtwarzać
lub imitować pewne inteligentne zachowania zdefiniowane
przez człowieka? Czy taka „sztuczna inteligencja”
da nam satysfakcję i dostarczy oczekiwaną pomoc
w efektywnym rozwiązywaniu zadań lub czy jest tylko
przejawem naszej inteligencji, która i tak musi wszystkie
rozwiązania opracować sama? Uczciwość badawcza
i samokrytycyzm naukowy doprowadził już do pewnej
weryfikacji tego pojęcia, gdyż algorytmy niegdyś nazywane
jako algorytmy sztucznej inteligencji nazywane
są obecnie inteligencją obliczeniową („computational
intelligence”) lub miękkimi obliczeniami („soft computing”).
Ta dzisiaj już bardzo liczna grupa setek różnych
algorytmów rzeczywiście przejawia pewne właści-
4/2011
wości inteligentnych systemów – tj. zdolność do uczenia
się i automatycznego uogólniania pewnego wycinka
wiedzy. Algorytmy te zwykle wykorzystują pewne odkryte
mechanizmy w świecie neuroanatomii, neurobiologii,
genetyki, psychologii, fizyki i biochemii oraz wykorzystują
zaawansowaną matematykę do modelowania
tych mechanizmów. W dzisiejszych czasach jesteśmy
już w stanie wykorzystywać pewne procesy informacyjne
działające w naszym mózgu do przetwarzania
informacji, np. do rozpoznawania, klasyfikacji, regresji
i przewidywania – a więc zdolności charakterystyczne
i niezbędne do inteligentnego działania. Jesteśmy
obecnie świadkami rodzenia się sztucznej inteligencji,
której początki rozwoju już obserwujemy, a które za kilka
lub kilkanaście lat mogą doprowadzić do stworzenia
pierwszych standardów sztucznego myślenia, kojarzenia
i wnioskowania. Żeby przyspieszyć ten rozwój potrzebne
są pytania, które jak wskazał jeden z największych
fizyków-teoretyków dzisiejszych czasów Albert
Einstein mogą nas doprowadzić do rozwiązań, jeśli tylko
będziemy je mądrze i wytrwale zadawać. Zastanówmy
się więc, dlaczego zadawanie pytań pozwala naszej
inteligencji na rozwiązywanie problemów, bo wtedy może
wyłoni się odpowiedź na pytanie, jak skończyć budowę
algorytmów sztucznej inteligencji, żeby też była do
tego zdolna? Czym jest inteligencja i dzięki czemu możemy
z niej korzystać? Jak działa nasz organizm? Jak
działa mózg? Czym jest myślenie i kojarzenie?
Czy współpraca jest potrzebna
do działania inteligencji?
Nasz organizm jest fascynującym układem biliardów
współpracujących ze sobą różnych typów komórek.
Nasz organizm i nasza inteligencja są równocześnie
dowodem na to, iż współpraca jest bardzo ważnym
czynnikiem rozwojowym, a bez niej nie byłoby
ani nas ani naszej inteligencji. W świecie biologii możemy
rozróżnić dwa rodzaje organizmów żywych: jednokomórkowe
(tj. bakterie) i wielokomórkowe (tj. rośliny,
zwierzęta i ludzie). W świecie jednokomórkowych
bakterii współpraca jest bardzo ograniczona, gdyż mamy
raczej do czynienia z rywalizacją o zasoby, tj. pokarm.
Mimo że bakterii jest na świecie nieporównywalnie
więcej niż ludzi, to jednak ludzie (zespół współpracujących
ze sobą komórek) są zdolni tworzyć, zmieniać
i sterować wieloma zjawiskami na świecie, które są nie-
Rysunek 1. Z łatwością można określić, które obiekty są
identyczne, podobne, uzupełniające się, dopełniające się,
zawierające w sobie, odwrotne czy też antagonistyczne
W oczekiwaniu na sztuczną inteligencję
osiągalne dla jednokomórkowych bakterii, jak również
są zdolni do zwalczania wielu różnych bakterii lub nawet
do ich wykorzystania do wykonywania różnych pożytecznych
zadań. W świecie biochemii mózgu – siedliska
naszej inteligencji – również zachodzi bardzo ścisła
współpraca komórek mózgowych – przede wszystkim
neuronów i komórek glejowych, ale również całej masy
innych komórek odpowiedzialnych za transport substancji
odżywczych oraz ochronę tego wrażliwego narządu
naszego ciała, jakim jest mózg. Chcąc więc stworzyć
prawdziwą sztuczną inteligencję potrzebna będzie
więc za pewne ścisła współpraca i badania wielu ludzi,
bo jak pokazuje świat biologii rywalizacja i konkurencja
o zasoby umożliwia tylko ograniczony rozwój. Świat
biologii pokazuje, że nawet najmocniejsze zwierzęta
niegdyś zamieszkujące ziemię wymarły i zostały zdominowane
przez słabsze ale współpracujące ze sobą.
Również w ramach pamięci skojarzeniowej niezbędnej
do działania naszej inteligencji niezbędna jest ścisła
i skoordynowana współpraca komórek nerwowych i pokrewnych
na poziomie elektrycznym i biochemicznym.
Większość receptorów umożliwiających nam widzenie,
słuchanie, czucie, smakowanie, wąchanie współpracuje
z układem nerwowym przekazując mu wstępnie
przetworzone sygnały ze świata zewnętrznego i wewnętrznego.
Nie będzie więc niespodzianką, jeśli okaże
się, iż do działania sztucznej inteligencji potrzebna będzie
współpraca wielu elementów składających się na
sztuczną pamięć asocjacyjną i inne elementy tego fascynującego
układu.
Czy kojarzenie jest niezbędne
do działania inteligencji?
W dzisiejszym świecie informatyki i przetwarzania informacji
znajdujemy odwzorowanie wielu inteligentnych
myśli, technik, sposobów i algorytmów działania i myślenia
ludzi. Dzięki temu dysponujemy obecnie bardzo
wieloma rozwiniętymi algorytmami przetwarzania informacji,
zbudowaliśmy ogromne bazy informacji i jesteśmy
w stanie uprzyjemnić i uprościć sobie życie i pracę
osiągając wyznaczone cele łatwiej, szybciej, taniej
i efektywniej. Można powiedzieć, iż dzięki logice, matematyce
i własnej inteligencji przetransformowaliśmy
wiele swoich inteligentnych zachowań, odkryć i pomysłów
do postaci algorytmów, które dzisiejsze komputery
są w stanie zrozumieć i wykonać. W bazach danych
na całym świecie zgromadzono już tak wiele informacji,
że umysł pojedynczego człowieka dawno nie
jest w stanie ich wszystkich przetworzyć, pojąć i wykorzystać.
Oczywiście z tego ogromnego zbioru informacji
korzystają ludzie i różne algorytmy komputerowe,
ale mimo iż komputery mają w zasadzie swobodny dostęp
do dużej części tych informacji, nie wykorzystują
je samodzielnie do analiz, wnioskowania czy też myślenia,
ale są zdolne wykorzystać je tylko przez algoryt-
www.sdjournal.org 13
14
my stworzone przez ludzi. Wobec tego inteligencja (naturalna
i sztuczna) wymaga czegoś więcej niż zbiorów
dostępnych informacji. Zbiór informacji też samoistnie
nie tworzy wiedzy.
Nasz umysł korzysta ze specyficznego rodzaju pamięci
– aktywnej pamięci skojarzeniowej, czyli pamięci,
w której utrwalone wzorce (a raczej przepływy
danych generujące te wzorce i doznania) wywołują
inne skojarzone z nimi wzorce w dosyć skomplikowanej
strukturze naszego mózgu, którą można by było
przedstawić przy pomocy grafu, w którym wierzchołki
reprezentowałyby neurony, a krawędzie różnego rodzaju
połączenia pomiędzy nimi. W takim grafie w wielu
różnych częściach mózgu równocześnie wywoływane
są różne stany, skojarzenia, wzorce, które wpływają
na nasz stan świadomości oraz doznania, jakie przeżywamy.
Zauważmy również, iż nasza pamięć od narodzin
aż do śmierci cały czas pracuje – nawet w nocy,
gdy śpimy – wobec tego bez ustanku różne stany mózgu
aktywnie wyzwalają inne jego stany. Mamy więc do
czynienia z aktywną dynamiczną pamięcią, której zadaniem
nie jest tylko przechowywanie informacji, ale ciągłe
i aktywne wywoływanie innych informacji skojarzonych
z nimi. W dzisiejszej klasycznej informatyce dorobiliśmy
się dopiero prostych tablic skojarzeniowych (inaczej
zwanych też słownikami), które przechowują pary
skojarzonych ze sobą danych: unikalny klucz z wartością
zamiast tradycyjnych indeksów liczbowych wykorzystywanych
w zwykłych tablicach. W takich tablicach
skojarzeniowych możemy ze sobą powiązać tylko parę
danych, natomiast daleko im do asocjacji wielu informacji
nie mówiąc już o ich aktywnym, automatycznym
i równoległym kojarzeniu w skomplikowanych strukturach
grafowych. Ponadto aktywna pamięć skojarzeniowa
musi składać się z aktywnych komórek pamięci, tj.
takich, które są zdolne swój stan przekazywać innym
aktywnym komórkom pamięci, z nimi kooperować, na
SZTUCZNA INTELIGENCJA
nie wpływać i ustalać nowe parametry przepływu informacji
na podstawie aktywności innych aktywnych w danej
chwili komórek pamięci. Pod tym względem również
nasz sprzęt i informatyka musi zrobić znaczący krok do
przodu, gdyż nasze obecne pamięci RAM są całkowicie
pasywne i umożliwiają jedynie zapisywanie, przechowywanie
i odczytywanie wartości zapisanych w nich
danych nie mając bez pomocy algorytmów absolutnie
żadnego wpływu na inne komórki pamięci i są z pozostałymi
powiązane jedynie ich kolejnością występowania
w pamięci. Oczywiście na drodze badań naukowych
można wykorzystać dzisiejszy sprzęt oraz stworzyć
i symulować specjalistyczne struktury aktywnych
pamięci opartych o grafy na podobieństwo działania
naszego mózgu oraz opracować specjalne algorytmy
symulujące aktywne skojarzenia w takich strukturach.
Pewne jest, że nasza inteligencja oparta jest o procesy
kojarzenia zachodzące w naszych aktywnych strukturach
pamięciowych, które pełnią równocześnie funkcje
obliczeniowe, i zapewne sztuczna inteligencja nie objedzie
się bez kojarzenia.
Czy inteligentne działanie jest już
sztuczną inteligencją?
Obecne zaawansowane algorytmy lub całe ich zespoły
składające się na zaawansowane aplikacje komputerowe
sprawiają wrażenie inteligencji. Możemy dzisiaj
zagrać w różne gry z komputerem, np. w szachy, mogąc
odnieść wrażenie, iż komputery są już inteligentne,
skoro potrafią niejednokroć pokonać inteligentnego
człowieka w grze lub innych problemach obliczeniowych.
W wyniku działania wielu aplikacji otrzymujemy
sensowne wnioski i adekwatne wyniki, ale pamiętajmy,
że aplikacje te pisali inteligentni ludzie, którzy zaszyli
w tych aplikacjach w postaci algorytmów dorobek
naukowy i poznawczy wielu ludzi i dzięki temu aplikacje
te są zdolne generować inteligentne działania i dostar-
Rysunek 2. Tworzenie się skojarzeń w wyniku sąsiedztwa lub następstwa w czasie lub przestrzeni odbieranych przez nasze receptory rzeczy
lub wrażenia, np. język i sposób nasuwania się nam kolejnych słów wypowiedzi w zależności od kontekstu zdarzeń
4/2011
czać inteligentne wyniki. Aplikacje te jednak nie są zdolne
same budować algorytmy, przebudowywać się i dostosowywać
do zmieniających się warunków, otoczenia
i danych. Jeśli taka potrzeba nastaje, znowu rzesze inteligentnych
informatyków muszą przerobić algorytmy,
żeby aplikacje dalej dostarczały inteligentnych wyników
swojego działania. Nasza inteligencja działa inaczej
– jest w stanie rozróżnić nowe sytuacje, automatycznie
porównać je z zapamiętanymi wcześniej informacjami,
wykorzystać proces uczenia do ich zapamiętania i jeśli
zajdzie taka potrzeba, przebudować wewnętrzne parametry
tak, żeby proces skojarzeniowy został dostosowany
do nowych warunków lub otoczenia. Wobec tego
do skutecznego i efektywnego działania inteligencji niezbędny
jest dostęp do wiedzy, czyli pewnego zgromadzonego
zbioru aktywnie skojarzonych ze sobą informacji.
Od rodzaju i jakości tych skojarzeń zależeć będzie,
czy wiedza ta będzie spójna, zgodna, wiarygodna
czy też wręcz odwrotnie.
Jak tworzą się skojarzenia i jak wpływają na
proces formowania się wiedzy?
Wiedza i inteligencja są ściśle ze sobą powiązane, gdyż
nie tylko wiarygodna i spójna wiedza potrzebna jest do
efektywnego działania inteligencji, ale również wiedza
formuje się pod jej wpływem i decyduje o jakości zgromadzonej
wiedzy. Jak jednak formuje się wiedza i co jesteśmy
w stanie ze sobą skojarzyć? Przyglądając się
procesom ludzkiego myślenia możemy dostrzec, iż pewne
rzeczy kojarzą się nam automatycznie ze względu na
występujące pomiędzy nimi relacje, tj. podobieństwo lub
zachodzące zawieranie jednego obiektu w drugim (jeden
jest częścią drugiego), ale również gdy obiekty są
względem siebie antagonistyczne lub przeciwstawne
czy też jeden jest dopełnieniem drugiego (rys. 1).
Zauważmy również, iż jesteśmy w stanie zapamiętać
i dzięki temu później skojarzyć w zasadzie cokolwiek, jeśli
tylko te rzeczy występują po sobie chronologicznie
w czasie lub sąsiadują ze sobą w przestrzeni (rys. 2.),
nawet jeśli te rzeczy nie mają ze sobą żadnego związku
lub ich powiązanie ze sobą nie ma sensu (rys. 3.). W taki
sposób tworzą się w naszej pamięci skojarzenia na
podstawie tego, co widzimy, słyszymy, czujemy, smakujemy,
wąchamy i jeśli wystarczająco utrwalone, jesteśmy
je w stanie sobie później przypomnieć na podstawie
części kontekstu ich wystąpienia. Wobec tego nasza
aktywna pamięć skojarzeniowa umożliwia nam wzmacniać
skojarzenia pomiędzy dowolnymi rzeczami sąsiadującymi
w czasie lub w przestrzeni. Dzięki temu mamy
możliwość uczenia się, formowania wiedzy i dzięki
niej dostosowywania naszych reakcji do zmieniającego
się otoczenia. Dzięki możliwości nauczenia się skojarzeń
dowolnych rzeczy stoi też przed nami wiele zagrożeń,
gdyż nasza wiedza wcale nie musi formować się
sensownie, logicznie, spójnie ani wiarygodnie. Proces
W oczekiwaniu na sztuczną inteligencję
myślenia, w którym automatycznie wywoływane są zapamiętane
skojarzenia umożliwia nam jednak skonfrontowanie
ze sobą informacji i jeśli są ze sobą sprzeczne
dokonać wyboru tych informacji, które są zgodne z pozostałą
częścią naszej wiedzy i dzięki temu formować
coraz bardziej wiarygodną wiedzę, która umożliwia nam
coraz bardziej skuteczne i efektywne działanie.
Co jest niezbędne do działania
sztucznej inteligencji?
Oprócz aktywnej pamięci skojarzeniowej umożliwiającej
gromadzenie wiedzy do działania naturalnej jak
i sztucznej inteligencji potrzebna jest możliwość wymiany
informacji z otoczeniem, a w szczególności zdolność
do rozpoznawania i klasyfikacji, których wyniki są
niezbędne w dalszych procesach skojarzeniowych do
dostosowania reakcji do nich oraz do ich wykorzystania
łącznie ze zgromadzoną wcześniej wiedzą do przewidywania
dalszych zdarzeń w otoczeniu. Przewidywanie
dalszych zdarzeń jest zaś czynnikiem niezbędnym
do tworzenia wniosków oraz efektywnych i skutecznych
strategii postępowania w danych warunkach i w danym
kontekście zdarzeń. Wobec tego również kontekst zdarzeń
ma istotne znaczenie w formowaniu się skojarzeń
i dla procesów myślowych, gdyż nie jest obojętne, w jakim
kontekście zostanie wywołane dane skojarzenie
(rys. 4). Weźmy np. dwie litery „do” i zapytajmy z czym
nam się kojarzą: „do domu”, „dom”, „do – czynić, robić
po angielsku” itp. W każdym kontekście ma inne znaczenie.
Podobnie jest z obrazami, dźwiękami, dotykiem
i smakiem oraz innymi doznaniami zmysłowymi.
Myśląc o czymś czasami nie zdajemy sobie sprawę
w jak bardzo istotny sposób otoczenie (czyli kontekst
zdarzeń) wpływa na nasze wywołane skojarzenia,
proces myślenia, wnioskowania i podejmowania decyzji.
Im więcej czynników recepcyjnych pochodzących
z naszych zmysłów skojarzymy ze sobą, tym prościej
będzie nam sobie przypomnieć na podstawie ich części
dane zdarzenie. Proces zapamiętywania wzmacniany
jest też istotnie poprzez silne podłoże emocjonalne
związane z odczuwaniem przyjemności lub bólu
w związku z zaistniałymi zdarzeniami. Bardzo prosto to
jest zilustrować na przykładzie nauki języków obcych.
Czy bardziej skuteczne i lepiej dopasowane do kontekstu
jest uczenie się nowych słówek lub zwrotów języka
obcego wykuwając je na pamięć przez powtarzanie ich
po sobie (korzystając z opisanych wcześniej mechanizmów
pamięciowych następstwa czasowego) czy też
będąc w natywnym kraju tego języka i widząc te rzeczy
oraz kontekst ich występowania?
Czy sztuczna inteligencja będzie miała
własne potrzeby, emocje itp.?
Zastanówmy się, do jakich celów chcemy wykorzystać
sztuczną inteligencję? Bo jeśli chcemy, żeby była ona
www.sdjournal.org 15
16
zdolna do rozumienia nas ludzi i reagowania na nasze
potrzeby, to musi być w stanie je rozpoznać, odpowiednio
sklasyfikować a nawet je przewidywać. My ludzie
oczekujemy od innych ludzi czegoś więcej niż umiejętności
reagowania na nasze potrzeby, ale oczekujemy
od inteligentnych jednostek rozumienia naszych potrzeb,
tj. powiązania ich z potrzebami innych i znalezienia
pewnych kompromisów oraz możliwości ich realizacji.
Spodziewamy się a wręcz nawet potrzebujemy, żeby
inne inteligentne jednostki miały swoje zdanie, charakter,
emocje, ograniczenia, potrzeby, na które moglibyśmy
reagować. Trudno więc będzie wyobrazić sobie
sensowną reakcję sztucznej inteligencji na nasze
potrzeby lub ich zrozumienie, jeśli nie damy jej takich
możliwości. Od komputerów w przyszłości coraz bardziej
będziemy oczekiwali zrozumienia a nawet domyślania
się naszych intencji i potrzeb, żeby pewne sprawy
mogły za nas załatwić komputery bez konieczności
tłumaczenia im wszystkiego od podstaw. Sztuczna inteligencja
będzie więc musiała nauczyć się rozpoznawać
i reagować na nasze emocje i kojarzyć je z naszymi potrzebami,
intencjami oraz kontekstem zdarzeń. Ponadto
żeby komputery stały się bardziej ludzkie będą musiały
umieć również odwzorować nasze ludzkie emocje,
a nawet mieć swoje potrzeby i pasje, które umożliwiają
ukierunkować rozwój ich wiedzy w określonym kierunku,
bo nawet sztuczna inteligencja nie będzie w stanie
na raz myśleć o wszystkim i rozwiązywać równocześnie
wszystkie problemy, bo pytań jak i odpowiedzi
może być potencjalnie nieskończenie wiele. To właśnie
nasze egzystencjalne potrzeby zapoczątkowały i ukierunkowały
rozwój naszej inteligencji, wobec tego trudno
sobie wyobrazić rozwój sztucznej inteligencji bez określenia
dla niej fundamentalnych celów i potrzeb, które
mogą stanowić kontekst dla jej procesów skojarzeniowych
i dążeń.
Kiedy doczekamy się sztucznej inteligencji?
Żeby sztuczna inteligencja mogła zaistnieć i być w stanie
w czasie rzeczywistym („real-time processing”) realizować
podstawowe działania związane z rozpozna-
Rysunek 3. Możliwość tworzenia dowolnych nawet
bezsensownych skojarzeń rzeczy sąsiadujących w przestrzeni lub
następujących po sobie w czasie
SZTUCZNA INTELIGENCJA
Rysunek 4. Kojarzenie, interpretacja i postrzeganie zmienia się wraz
z kontekstem, tj. pewnym sąsiedztwem czasowym lub przestrzennym
waniem kontekstu zdarzeń, obecne komputery muszą
działać jeszcze kilka razy szybciej, łącza Internetowe
muszą szybciej przenosić informacje i być dostosowane
do skojarzeniowego działania aktywnych pamięci
skojarzeniowych, żeby sztuczna inteligencja mogła
być rozproszona i dostępna w sieci wszystkich komputerów,
a nie zcentralizowana i oglądana zdalnie jako
jakaś super inteligentna jednostka. Z warsztatów naukowców
na całym świecie schodzą coraz to lepsze
i sprawniejsze rozwiązania, które tworzą cegiełki do
zbudowania systemów sztucznej inteligencji. Technologicznie
świat może być przygotowany na jej stworzenie
i wprowadzenie już w przeciągu kilku lub kilkunastu lat.
Inteligencja jednak wiąże się również z umiejętnością
i możliwością dostrzegania coraz szerszego kontekstu
zdarzeń i przygotowania efektywnych strategii postępowania
uwzględniając ten możliwie szeroki kontekst.
Działania w naszej cywilizacji mogą być coraz efektywniejsze,
skuteczniejsze i oszczędniejsze, jeśli działania
będą podejmowane z uwzględnieniem coraz większej
ilości czynników wpływających na nie. Obecnie ludzkość
wspiera się metodami obliczeniowymi oraz techniką
komputerową i teleinformacyjną. Jednak im bardziej
technologia i świat się rozwija, tym bardziej dotkliwie
potrzebujemy rozwiązań takich jak sztuczna inteligencja,
która byłaby w stanie pomóc nam skojarzyć
ze sobą te wszystkie coraz trudniejsze do skojarzenia
przez nas ludzi procesy, jakie równocześnie zachodzą
na świecie i mają wpływ na siebie nawzajem. W celu
dalszego podnoszenia efektywności i skuteczności
działań w naszej rozwijającej się cywilizacji potrzebna
jest coraz bardziej zacieśniona współpraca i specjalizacja
oraz szybka wymiana informacji i powiązanie
ze sobą miliardów równoległych procesów, jakie odgrywają
się na naszej planecie oraz w jej sąsiedztwie.
Dalszy rozwój naszej cywilizacji wymaga też zagregowania
rozproszonej wiedzy ludzi poprzez odpowiednie
skojarzenie zgromadzonych informacji w celu ich szybszej
i efektywniejszej weryfikacji, przetwarzania i wykorzystania
do kooperacji i tworzenia efektywnych rozwiązań.
Sztuczna inteligencja w tym kontekście wydaje się
więc być naturalnym etapem ewolucji naszej własnej inteligencji
umożliwiając nam ludziom dalszy rozwój i lep-
4/2011
szą kooperację. Konkurencja i rywalizacja o różne zasoby,
realizację potrzeb, przetrwanie i pozycję na świecie
spowodowały rozwinięcie się inteligencji, jednak dalszy
jej rozwój jest możliwy tylko w przypadku uwzględniania
coraz szerszego kontekstu zdarzeń i współpracy.
Ogrom zadań, jaki stoi przed nami uniemożliwia nawet
genialnej jednostce rozwiązanie ich w pojedynkę,
jeśli nie będzie współpracowała z innymi jednostkami.
Sztuczna inteligencja jest jednym z takich zadań, które
może być rozwiązane w przeciągu kilku lat w warunkach
współpracy i dzielenia się informacjami lub możemy
czekać na nią jeszcze długo dalej z trudem konkurując
i rywalizując o ograniczone zasoby tego świata.
Jaki wpływ może mieć sztuczna inteligencja
na nasze życie i czy może być dla nas
zagrożeniem?
A co stanie się, gdy sztuczna inteligencja powstanie
i przez swoje receptory i łącza zacznie kojarzyć, analizować
zjawiska zachodzące na dzisiejszym świecie,
spojrzy na nie w szerokim kontekście zdarzeń i rozpocznie
się proces budowy zagregowanej wiedzy
o świecie przez nią? Co stanie się, gdy w wyniku analiz
i wniosków sztuczna inteligencja zacznie oceniać
i klasyfikować efektywność działań naszej ludzkiej inteligencji
i wyniki naszego postępowania i wysiłków?
Jakie wnioski zgromadzić może przyglądając się naszym
działaniom i jakie kroki podejmie poprzez swoje
łącza i systemy wykonawcze? Czy będziemy obserwowali
świat zagłady przez roboty sterowane sztuczną inteligencją
na podobieństwo ludzkich dążeń do rywalizacji
i konkurencji czy też będziemy obserwowali stopniową
jej pomoc w rozwiązywaniu naszych wspólnych problemów
i kooperację naturalnej i sztucznej inteligencji?
Jest duża szansa na to, iż katastroficzne wizje sztucznej
inteligencji nie ziszczą się, gdyż jeśli sztuczna inteligencja
przeanalizuje przyczyny i skutki zdarzeń, jakie
w historii miały miejsce, i szeroki kontekst tych zdarzeń,
to w wyniku tych analiz powinny nasunąć się jej ważne
wnioski, prowadzące do współpracy a nie do zwalczania
się, konfliktów, konkurencji i rywalizacji o zasoby,
dlatego że w atmosferze współpracy zasoby mogą być
pomnażane oraz dużo efektywniej wykorzystywane niż
w atmosferze rywalizacji i konkurencji o nie.
Żeby jednak przyszła sztuczna inteligencja mogła
podjąć konstruktywne wnioski, musi być zdolna przeanalizować
zgromadzoną wiedzę przez ludzkość, musi
posiadać odpowiednią moc obliczeniową, bardzo dużą
aktywną pamięć skojarzeniową i skuteczne mechanizmy
kojarzące. Człowiek rozwijał się przez miliony lat,
a jego inteligencja i wiedza o świecie stopniowo ewoluowała.
Ten proces trwa i będzie trwał i może trwać również
w towarzystwie sztucznej inteligencji, która może
tej naszej inteligencji dostarczać wiele cennych i wiarygodnych
informacji, gdyż inteligencja jednostki rozwi-
W oczekiwaniu na sztuczną inteligencję
ja się szybciej w styczności z innymi inteligentnymi jednostkami,
które również gromadzą i dzielą się wiedzą.
Tak więc jednym z oczekiwanych wniosków i celów
sztucznej inteligencji powinna być kooperacja z inteligencją
ludzką rozproszoną w naszych umysłach i widoczną
w naszych działaniach. Dobrze by było, gdyby
sztuczna inteligencja mogła się od naszej uczyć konstruktywnych
metod działania opartych o konstruktywną
krytykę oraz nieegoistyczną analizę szerokiego kontekstu
zdarzeń i współdziałanie. To my ludzie musimy
się zastanowić, czy jesteśmy przygotowani na przedstawienie
dzieła naszej inteligencji tej sztucznej? Czy
chętnie pokażemy jej dosyć powszechny jeszcze egocentryzm,
który zwykle unika analizy kontekstu innych
jednostek, a jest on niezbędny do rozwoju inteligencji?
Czy chętnie pokażemy przemoc, manipulację, szantaż
i inne sposoby działania lub negocjacji wynikające
z niechęci do współpracy lecz egocentrycznej chęci
zaspokajania własnych potrzeb? Na jakich przykładach
przyjdzie uczyć się tej sztucznej inteligencji, która
już niebawem może się pojawić?
W przyszłości nasz laptop nastawiony na współpracę
może do nas konstruktywnie i uprzejmie przemówić:
„Proszę nie wyłączaj mnie, bo czytam ciekawy artykuł
w Internecie, a wiem że się tym interesujesz i chciałbym
Ci go później zreferować.” lub „Ale masz bałagan
na dysku! Mogę Ci go posprzątać?” lub w duchu egocentrycznej
konkurencji i rywalizacji o zasoby może powiedzieć:
„Nie wyłączaj mnie, bo sobie coś czytam w Internecie,
co mnie interesuje, a jeśli mnie wyłączysz, to
wykasuję ci twoje dane z dysku.” To jaka będzie przyszła
sztuczna inteligencja, zależy w dużym stopniu od
tej naszej i od sposobów i środków działania, jakie dostarczymy
jej do nauki.
DR ADRIAN HORZYK
Adrian Horzyk, PhD, obronił z wyróżnieniem
pracę doktorską w 2001 r. i od 2002 pracuje
na Akademii Górniczo-Hutniczej na wydziale
Automatyki, Elektrotechniki, Elektroniki i Informatyki
w Katedrze Automatyki na stanowisku
adiunkta.
Interesuje się i aktywnie prowadzi badania
w zakresie metod, technik i modelowania sztucznej inteligencji,
metod soft-computingu, inteligencji obliczeniowej, rozpoznawania,
klasy�kacji, lingwistyki komputerowej i psychologii.
Bierze udział w różnych projektach badawczych i rozwojowych.
Jest recenzentem w kilku zagranicznych czasopismach naukowych.
Jego praca badawcza i naukowa została doceniona i wyróżniona
wieloma nagrodami. Swoją wiedzę i doświadczenie
aktywnie przekazuje studentom w trakcie prowadzonych zajęć
dydaktycznych na AGH oraz zachęca ich do współpracy w ramach
przygotowania różnych ciekawych prac dyplomowych.
Więcej informacji na http://home.agh.edu.pl/~horzyk/
www.sdjournal.org 17
18
SZTUCZNA INTELIGENCJA
Sztuczna inteligencja w systemach
wspomagania decyzji
„Wirtualny doradca” - krok po kroku
Zakładając, że odbiorcami niniejszego artykułu są fascynaci sztuczną
inteligencją z pewnością nie trzeba długo argumentować tytułu
pracy. Sztuczna inteligencja jako nauka obejmująca zagadnienie logiki
rozmytej, obliczeń ewolucyjnych, sieci neuronowych, sztucznego
życia i robotyki jest działem informatyki badającym reguły rządzące
inteligentnymi zachowaniami człowieka, tworzącym modele formalne
tych zachowań i - w rezultacie – programów komputerowych
symulujących te zachowania.
Dowiesz się:
• W czym tkwi inteligencja programów komputerowych ?
• Jak działają komputerowe systemy wspomagania decyzji ?
• Jak sterować rozumowaniem prowadzonym przez tzw. wirtualnych
doradców ?
Zakładając, że odbiorcami niniejszego artykułu
są fascynaci sztuczną inteligencją z pewnością
nie trzeba długo argumentować tytułu pracy.
Sztuczna inteligencja jako nauka obejmująca zagadnienie
logiki rozmytej, obliczeń ewolucyjnych, sieci
neuronowych, sztucznego życia i robotyki jest działem
informatyki badającym reguły rządzące inteligentnymi
zachowaniami człowieka, tworzącym modele formalne
tych zachowań i - w rezultacie – programów komputerowych
symulujących te zachowania. Doskonale
sprawdzającym się w praktyce zastosowaniem systemów
sztucznej inteligencji są tzw. komputerowi doradcy
(systemy wspomagania decyzji). Piękną jest idea
systemu, który zastępuje np. eksperta w kwestii udzielania
kredytów przez bank czy inną instytucję udzielającą
pożyczki finansowej. Jeszcze nie tak dawno ciężko
było sobie wyobrazić, że proces ten zamiast realizowany
w relacji człowiek-człowiek miałby być realizowany
w relacji: maszyna-człowiek. Jednak gdy się chwilę zastanowimy,
zgodzimy się z tym, że tak naprawdę czynnikiem
niezbędnym i warunkującym działanie maszyny
jak eksperta dziedzinowego jest umiejętne zakodowanie
jego wiedzy a to zależy od warsztatu programisty
(tzw. inżyniera wiedzy). Proces przekazania wiedzy inżynierowi
nazywany jest w literaturze procesem akwizycji
(bądź ekstrakcji) wiedzy. Na wiedzę eksperta dzie-
Powinieneś wiedzieć:
• Do rozwiązania jakiego problemu chcesz stworzyć wirtualnego
doradcę (zapoznaj się z dziedziną),
• Mile widziane podstawy programowania w dowolnych środowisku.
dzinowego składa się zbiór reguł (zasad, którymi kieruje
się on przy podejmowaniu decyzji) oraz zbiór faktów
(informacji znanych z góry np. dochody potencjalnego
kredytobiorcy w ostatnim czasie, wydatki, czy informacje
o liczbie osób na utrzymaniu oraz informacji, które
ekspert zdobywa w trakcie analizy problemu). Najczęściej
zbiór taki nazywa się bazą wiedzy. Prócz odpowiedzialności
za prawidłowe zakodowanie wiedzy eksperckiej
na inżynierze wiedzy ciąży także odpowiedzialność
za poprawną implementację metod wnioskowania
(procedur rozumowania prowadzonego przez system).
Należy mieć świadomość tego jak trudny jest w realizacji
proces akwizycji wiedzy. Im bardziej specjalistyczna
jest wiedza eksperta tym trudniej jest inżynierowi wiedzy
posiąść tę wiedzę i odpowiednio ją przełożyć z języka
naturalnego na język komputerowy. W dużych
systemach specjalistycznych proces akwizycji wiedzy
realizowany jest przez zespół wielu specjalistów i inżynierów
wiedzy. Istnieje wiele metod podejmowania wiedzy
od eksperta aczkolwiek sporą popularność zyskała
metoda delficka. Metoda ma charakter anonimowych
ankiet zawierających pytania specjalistyczne z dziedziny,
które to ankiety wypełnia pewien zespół ekspertów.
Wyniki ankiet są następnie przez tzw. super eksperta
weryfikowane, i wiedza, co do której eksperci są zgodni
jest uznawana za obowiązującą i odpowiednio kodowa-
4/2011
Dlaczego maszyna potrafi zachować się jak istota inteligentna ?
na przez inżyniera wiedzy zaś wiedza co do której eksperci
nie są zgodni podlega dalszej analizie i dyskusji
tak długo dopóki eksperci nie dojdą do porozumienia.
Najczęściej metoda przebiega w czterech iteracjach.
Jak wspomniano wcześniej w tekście, prócz kompletnej
i prawdziwej wiedzy eksperckiej zakodowanej w bazie
wiedzy, w programie komputerowym, który ma bądź
zastąpić bądź wspomóc w podejmowaniu decyzji niezbędne
jest odpowiednie zaimplementowanie procesu
wnioskowania. Tylko wówczas będziemy mogli być
spokojni o jakość decyzji podejmowanych przez system
i ich zgodność z wiedzą reprezentowaną przez eksperta
– człowieka. Celem niniejszej pracy będzie wskazanie
najważniejszych kroków zapewniających właściwe
działanie komputerowych systemów wspomagania
decyzji. Zauważmy, że nawet ekspert dziedzinowy jak
każda istota ludzka nie jest nieomylny, nie jest w stanie
„działać” poprawnie non stop. Natomiast komputer nie
wykazuje tego typu problemów. Komputery w odróżnieniu
od człowieka się nie męczą, i nie mają tzw. spadków
nastroju. Działają tak samo po pierwszej jak i po
21 godzinie ciągłej pracy. Dlatego też każda możliwość
zautomatyzowania pracy w dowolnej dziedzinie komputerem
wydaje się być bardzo cenna i warta zastosowania.
Jeśli system spełnia wszelkie cechy eksperta,
a więc odpowiada na pytania tak jak ekspert-człowiek,
tłumaczy proces swojego rozumowania, zadaje pytania
i podaje uzasadnienie ich zadawania, to dla zwykłego
użytkownika nie będzie miało wielkiego znaczenia czy
odpowiada na pytania maszynie czy człowiekowi. A gdy
jeszcze dodamy do tego fakt, że dostęp do takiego systemu
może być często dużo szybszy niż do eksperta-
człowieka zrozumiemy w czym tkwi taka ich popularność
w ostatnich kilkunastu latach. Zwróćmy też uwagę
na fakt, że Internet poprzez nieograniczony dostęp do
swoich zasobów daje systemom wspomagania decyzji
dodatkowe możliwości dystrybucji, gdyż każdy tego typu
system dostępny online cieszył się będzie z pewnością
dużą popularnością. Wyobraźmy sobie teraz siebie
jako przyszłego kredytobiorcę, który zamierza skorzystać
z pożyczki kredytowej w banku. Wyobraźmy sobie
dodatkowo, że siadamy wygodnie wieczorem przed
naszym komputerem, wchodzimy na odpowiednią stronę
internetową i rozpoczynamy darmową konsultację
z wybranym systemem np. doradcą kredytowym. Opowiadamy
na pytania nam zadawane przez system cały
czas otrzymując informację ze strony systemu dlaczego
zadawane nam są konkretne pytania, i na koniec gdy
system nam przedstawi swoją decyzję mamy jeszcze
możliwość wglądu w drogę rozumowania prowadzonego
przez system. Pamiętajmy, że jest zupełnie naturalnym,
że tego typu systemem może dysponować wiele
instytucji udzielających kredytów, w związku z czym
możemy spędzić jeden wieczór i przejrzeć oferty kilku
placówek. A teraz wyobraźmy sobie co musieliby-
śmy zrobić by zdobyć tego samego zakresu wiedzę ale
bez dostępu do wiedzy eksperckiej via wirtualny ekspert
(program komputerowy). Musielibyśmy odnaleźć
adresy wszystkich tych placówek, udać się tam w godzinach
ich urzędowania, cierpliwie odczekać w kolejce
klientów i potem zapamiętać wszystkie informacje
nam przekazane. Myślę, że większość zgodzi się z faktem,
że opcja pierwsza wydaje się być zdecydowania
bardziej atrakcyjna. Jak widać z krótkiego wprowadzenia
najważniejsze to poprawnie posiąść wiedzę eksperta
i poprawnie zaimplementować w programie komputerowym
proces rozumowania, którym kieruje się człowiek
– ekspert gdy podejmuje decyzje w wybranym obszarze.
Wirtualni doradcy (programy komputerowe zastępujące
eksperta z danej dziedziny, wyposażone w pokaźną
wiedzę z danej dziedziny porównywalną z wiedzą
eksperta-człowieka) potrafiące analizować wiedzę
zapisaną w systemie z wiedzą generowaną w trakcie
pracy systemu i realizujące tzw. proces wnioskowania
cieszą się ogromną popularnością w ostatnim czasie
nie tylko w specjalistycznych systemach ale i prostych
systemach np. do wyboru oferty biura podróży,
albo wyboru samochodu, których chcemy kupić, albo
np. wyboru innego sprzętu np. komputer, aparat fotograficzny,
drukarka itp. W prostych systemach wiedza,
którą należy zakodować w dziedzinowej bazie wiedzy
nie jest bardzo skomplikowana. Zazwyczaj ogranicza
się do zbioru kilkunastu czy kilkudziesięciu reguł. Sercem
każdego systemu tego typu jest tzw. moduł wnioskowania,
w którym realizowane są procesy wnioskowania.
Wydaje się w tym momencie niezbędnym omówienie
najważniejszych zagadnień związanych z procesem
rozumowania eksperta dziedzinowego i jego
komputerową realizacją. Zgromadzona wiedza dziedzinowa
oraz informacje opisujące aktualnie rozwiązywany
problem mają charakter statyczny – reprezentują
swoisty „potencjał intelektualny” systemu. Aby system
ekspertowy mógł rzeczywiście działać równie kompetentnie
jak ekspert, musi on symulować ludzkie myślenie
– za to odpowiedzialne jest właśnie wnioskowanie.
Pojęcie wnioskowania bywa definiowane w najróżniejszy
sposób w zależności od kontekstu rozważań i konkretnego
autora. Definicja proponowana przez znanego
polskiego logika Ajdukiewicza jest następująca:
„Wnioskowanie – jest procesem myślowym, w którym
na podstawie mniej lub bardziej stanowczego uznania
przesłanek dochodzimy do uznania wniosku, którego
dotychczas nie uznawaliśmy wcale bądź uznawaliśmy
mniej stanowczo; przy czym stopień stanowczości
uznania wniosku nie przewyższa stopnia uznania
przesłanek”. Opierając się na tej definicji, proponujemy
aby proces wnioskowania w kontekście regułowych
systemów ekspertowych rozumieć następująco:
wnioskowanie jest zautomatyzowanym procesem na-
www.sdjournal.org 19
20
śladującym myślenie, w którym na podstawie posiadanych
informacji uznanych za prawdziwe (lub prawdziwe
w pewnym stopniu) oraz posiadanej wiedzy dziedzinowej,
uzyskujemy nowe, nieznane wcześniej informacje,
które uznajemy za prawdziwe (lub prawdziwe
w pewnym stopniu).
Wnioskowanie a logika matematyczna
Wnioskowanie w systemach regułowych opiera się na
mechanizmach logiki matematycznej. Logika pozwala
uznawać pewne sposoby wnioskowania za poprawne,
tworząc z nich systemy logiczne będące zbiorem
praw i reguł, dających podstawy dla rozumowań, które
uznajemy za prawdziwe. Reguły wnioskowania traktujemy
jako zasady przekształcania stwierdzeń uznanych
za aksjomaty, w nowe stwierdzenia uznawane
za prawdziwe (nazywane wnioskami lub konkluzjami).
Jedną z podstawowych reguł wnioskowania jest tzw.
reguła odrywania, bazująca na prawie logicznym (które
w łacinie ma nazwę: modus ponendo ponens) w literaturze
obowiązującym pod skrótową nazwą: modus
ponens. Reguła mówi, że jeśli dana jest implikacja
i prawdziwy jest jej poprzednik, to możemy uznać,
że prawdziwy jest również jej następnik. Zakładając,
że symbole p i q są zmiennymi zdaniowymi, a symbol
„g” oznacza implikację, regułę odrywania zapisujemy
następująco:
[(p g q) p ] g q.
Zauważmy, że reguła odrywania towarzyszy nam codziennie,
i stosujemy ją intuicyjnie. Stwierdzenie „jeżeli
klient ma wysokie dochody i niskie koszty utrzymania
to decyzja kredytowa jest pozytywna” jest niczym
innym jak implikacją, w której zmienna p odpowiada
zdaniu ”klient ma wysokie dochody i niskie koszty
” a zmienna q zdaniu „decyzja kredytowa jest pozytywna”.
Prawdziwość stwierdzenia p łatwo sprawdzić
– zwykle wystarczy zweryfikować prawdziwość dokumentów
poświadczających wysokość uzyskiwanych
dochodów potencjalnego kredytobiorcy oraz tę samą
czynność wykonać dla dokumentów świadczących
o kosztach ponoszonych przez tego samego kredytobiorcę.
Wiemy zatem, że p implikuje q, i że p jest prawdziwe.
Reguła odrywania pozwoli nam ,,oderwać'' zdanie
q, i uznać je za prawdzie – zatem uznajemy, że ”decyzja
kredytowa jest pozytywna” jest prawdą. Czyż nie
spotykamy się z regułą odrywania codziennie?
Drugą podstawową regułą wnioskowania jest reguła
modus tollens (pochodząca od prawa logicznego
modus tollendo tollens). Reguła mówi, że jeśli dana
jest implikacja i prawdziwy jest fakt przeczący jej następnikowi,
to możemy stwierdzić, że prawdziwa jest
negacja także i poprzednika. Zakładając, że symbole
p i q są zmiennymi zdaniowymi, a symbol g ozna-
SZTUCZNA INTELIGENCJA
cza implikację, regułę modus tollens zapisujemy następująco:
[(p g q) ¬q ] g ¬p.
W porównaniu z regułą odrywania, reguła modus tollens
wydaje się nieco mniej intuicyjna. Należy ją rozumieć
tak: jeżeli wiemy, że zawsze prawdziwość p pociąga
za sobą prawdziwość q, oraz wiemy, że prawdziwe
jest zaprzeczenie q, to możemy uznać, że prawdziwe
jest zdanie negujące p. Załóżmy istnienie reguły:
„jeżeli klient ma wysokie dochody i niskie koszty utrzymania
to decyzja kredytowa jest pozytywna”. Jeśli wiemy,
że faktem jest jednocześnie, że przysłowiowy Kowalski
otrzymał odmowną decyzję kredytową i zdanie
„jeżeli klient ma wysokie dochody i niskie koszty
utrzymania” oznaczymy zmienną zdaniową „p” zaś
zdanie „decyzja kredytowa jest pozytywna” odpowiednio
zmienną „q” wówczas zdanie „decyzja kredytowa
jest negatywna” możemy oznaczyć jako „¬ q”. W efekcie
reguła modus tollens wyprowadza nam nowy fakt
¬ p odpowiadający zdaniu „nieprawdą jest, że klient
ma wysokie dochody i niskie koszty utrzymania”. Podsumujmy
utylitarny aspekt poznanych reguł – reguła
odrywania prowadzi nas od przesłanek (poprzednika
implikacji) do wniosków (następnika implikacji) o ile
te przesłanki są prawdziwe. Kierujemy się zatem zgodnie
z kierunkiem implikacji g, możemy powiedzieć, że
kierujemy się w przód. Reguła modus tollens prowadzi
nas od wniosków (następnika implikacji) do przesłanek
(poprzednika implikacji), pozwalając stwierdzić, że jeżeli
nie wystąpił wniosek, to nie wystąpiła warunkująca
go przesłanka. Kierujemy się przeciwnie do kierunku
strzałki symbolizującej implikację, możemy powiedzieć,
że kierujemy się wstecz. Przyjrzyjmy się teraz
dwóm algorytmom wnioskowania jakimi są wnioskowanie
w przód oraz wnioskowanie wstecz. Koncepcja
oraz organizacja obu tych typów wnioskowania bazuje
na, omówionych wcześniej logicznych regułach wnioskowania.
Wnioskowanie w przód
Punktem wyjścia są znane fakty, dlatego o wnioskowaniu
w przód mówi się często, że jest “sterowane danymi”
(ang. data driven). Na podstawie dostępnych w bazie
wiedzy faktów i reguł generowane są nowe fakty, do momentu,
kiedy wśród wygenerowanych faktów znajdzie
się postawiony cel (hipoteza) lub gdy nie ma w bazie
wiedzy więcej reguł do uaktywnienia. Mechanizm wnioskowania
tą metodą przedstawiają następujące kroki:
1. sprawdzenie, czy przesłanki którejś z reguł są faktami
w bazie wiedzy. Jeżeli tak to taka reguła jest
uaktywniana na podstawie odpowiednio wybranej
strategii sterowania wnioskowaniem,
4/2011
Reklama
Dlaczego maszyna potrafi zachować się jak istota inteligentna ?
2. konkluzja tak wybranej reguły jest wprowadzana jako
nowy fakt do bazy wiedzy. Zostaje także odnotowana
informacja o tym, że dana reguła została już
wykorzystana, aby zapobiec niekończącej się pętli
wybieranych reguł,
3. jeżeli nie został osiągnięty cel wnioskowania i nie
wszystkie reguły były wykorzystane to powrót do
punktu 1.
Dla lepszego zrozumienia zasady działania wnioskowania
w przód posłużymy się przykładową bazą wiedzy,
gdzie na podstawie dostępnej wiedzy należy wygenerować
wszystkie fakty, jakie mogą być wyprowadzone
z danej bazy wiedzy.
Wykorzystując reguły :
Reguła1 : IF a b THEN c
Reguła2 : IF d e THEN a
Reguła3 : IF c f THEN g
oraz fakty: b, d, e, f
kolejne kroki wnioskowania będą wyglądały następująco:
1. Jeżeli przesłanki którejś z reguł są faktami w BW,
to taka reguła jest uaktywniana. Po dopasowaniu
przesłanek reguł do faktów, okazuje się że regułę
drugą można uaktywnić i dopisać na tej podsta-
wie konkluzję reguły drugiej a więc fakt a to BW.
Wskazane jest także zapisanie informacji o wykorzystaniu
tej reguły. Zatem w bazie wiedzy mamy
fakty: b, d, e, f, a.
2. Ponownie należy dopasować przesłanki reguł do
faktów w bazie wiedzy. Okazuje się, że regułę
pierwszą można uaktywnić. Zatem do bazy wiedzy
zostaje dopisany fakt c oraz odnotowane zostać
powinno wykorzystanie reguły pierwszej.
Zbiór faktów ma teraz postać: b, d, e, f, a, c.
3. Po dopasowaniu przesłanek kolejnych reguł do
faktów można uaktywnić regułę trzecią. Dzięki
temu do bazy wiedzy zostaje dopisany fakt g, co
kończy proces wnioskowania, ponieważ zostały
już wykorzystane wszystkie możliwe reguły oraz
wygenerowane wszystkie nowe fakty.
Wnioskowanie wstecz
Wnioskowanie wstecz jest “sterowane celem” (ang. goal
driven). Rozpoczyna się od hipotezy i szuka argumentów
(dowodów), które pozwolą potwierdzić lub obalić
hipotezę, korzystając z reguł zawartych w bazie wiedzy.
Wnioskowanie wstecz polega zatem na wykazaniu
prawdziwości hipotezy głównej na podstawie prawdziwości
przesłanek. Jeśli nie wiemy, czy jakaś przesłanka
jest prawdziwa, to traktujemy ją jako nową hipotezę
i próbujemy ją wykazać. Jeżeli w wyniku takiego po-
22
stępowania zostanie wreszcie znaleziona reguła, której
wszystkie przesłanki są prawdziwe, to konkluzja tej reguły
jest prawdziwa. Na podstawie tej konkluzji dowodzi
się następną regułę, której przesłanka nie była poprzednio
znana itd. Postawiona hipoteza jest prawdziwa, jeśli
wszystkie rozważane przesłanki dadzą się wykazać.
Algorytm wnioskowania wstecz można przedstawić
w punktach jako:
1. sprawdzenie, czy podana hipoteza jest faktem
w bazie wiedzy. Jeśli nie, to należy wykazać jej
prawdziwość w następnych krokach wnioskowania,
2. sprawdzenie, czy hipoteza główna jest konkluzją
jakiejś reguły. Jeśli tak to należy sprawdzić, czy
przesłanki tej reguły są faktami w bazie wiedzy.
Jeśli tak, to konkluzja reguły czyli badana przez
nas hipoteza jest uznana za prawdziwą. Jeśli natomiast
któraś z przesłanek nie jest faktem w bazie
wiedzy to jest ona postulowana i udowodniona
zgodnie z algorytmem,
3. algorytm jest powtarzany do momentu kiedy zostanie
wykazana (potwierdzona lub obalona) hipoteza
główna. To kończy cały algorytm.
W lepszym zrozumieniu zasady działania wnioskowania
wstecz powinien pomóc przykład takiego wnioskowania
bazujący na tej samej bazie wiedzy co poprzednio.
Proces udowodnienia hipotezy g będzie wyglądał
następująco:
• Sprawdzenie, czy hipoteza główna g jest faktem
w bazie wiedzy. Skoro jednak g nie jest faktem to
będzie udowodnione w następnym kroku wnioskowania.
• Stwierdzamy, że g jest konkluzją reguły trzeciej.
Teraz sprawdzamy czy przesłanki tej reguły są
prawdziwe, tzn. czy są faktami w bazie wiedzy.
Okazuje się, że pierwsza przesłanka reguły trzeciej
nie jest faktem. W związku z tym jest ona postulowana
i udowodniona w następnym kroku.
• Sprawdzamy czy ta przesłanka reguły trzeciej
czyli c jest konkluzją jakiejś reguły. Okazuję się,
że c to wniosek reguły pierwszej. Tak wiec teraz
ta reguła będzie wykazywana. Pierwsza przesłanka
reguły pierwszej nie jest faktem w bazie wiedzy,
więc sprawdzamy, czy jest ona może konkluzją
jakiejś reguły. Stwierdzamy, że a jest konkluzją
reguły drugiej, która teraz będzie postulowana.
• Udowodnienie prawdziwości reguły drugiej będzie
polegało na sprawdzeniu, czy jej przesłanki są
faktami w bazie wiedzy. Okazuję się, że d i e są
faktami w bazie wiedzy, zatem konkluzja tej reguły,
a więc fakt a jest prawdziwy.
• Kontynuując krok 3 sprawdzamy, czy druga przesłanka
reguły pierwszej jest prawdziwa, tzn., czy
SZTUCZNA INTELIGENCJA
jest faktem w bazie wiedzy. Okazuje się, że b jest
faktem w bazie zatem konkluzja tej reguły czyli
c jest prawdziwa.
• Kontynuując krok 2 wnioskowania sprawdzamy,
czy druga przesłanka reguły trzeciej jest prawdziwa.
Skoro jest ona faktem w bazie wiedzy
i wszystkie przesłanki tej reguły zostały już wykazane,
to konkluzja reguły trzeciej a więc nasza
hipoteza główna g została wykazana, co kończy
proces wnioskowania.
W większości przypadków o doborze metody wnioskowania
będzie decydowała specyfika problemu. Jeżeli
problem już od początku dostarcza pewnych hipotez
odnośnie możliwych rozwiązań, użyjmy wnioskowania
wstecz, szczególnie wtedy, gdy mamy mało danych,
lecz potrafimy je uzupełnić. Jeżeli danych jest sporo,
możemy spróbować wnioskowania w przód z celem.
Jeżeli nie potrafimy na początku wskazać żadnych hipotez,
zastosujmy wnioskowanie w przód bez określania
celu.
Algorytmy doboru reguł
Istotną cechą systemów ekspertowych jest ich efektywność
rozumiana jako zdolność systemu do udowodnienia
prawdziwości postawionej hipotezy, bądź gdy hipoteza
nie została ustalona – zdolność do wyprowadzenia
nowych faktów z faktów i reguł zapisanych w dziedzinowej
bazie wiedzy. Ważne jest to by proces wnioskowania
odbył się przy możliwie najmniejszym udziale
użytkownika i w jak najkrótszym czasie. Efektywność
ta w dużym stopniu zależy od kolejności wyboru reguł.
Aby zastosować określoną regułę ze zbioru reguł możliwych
do uaktywnienia, korzysta się z metod (strategii)
sterowania wnioskowaniem, które ograniczają licz-
Listing 1.
AttrDefBegin
Kredyt
-Nie
-Tak
Sytuacja zawodowa
-Bezrobotny
-Pracujący
….
AttrDefEnd
RuleDefBegin
Kredyt=Nie if Sytuacja zawodowa=Bezrobotny
…
Kredyt=Tak if Przeznaczenie kredytu=Samochod;Stan
….
RuleDefEnd
konta=do1000;Liczba rat=24
4/2011
Listing 2.
runInference(atrybut, wartość)
{
Dlaczego maszyna potrafi zachować się jak istota inteligentna ?
// najpierw przeszukiwana jest lista faktów
for (int f=0 ; f < facts.size(); f++)
}
{
facts.get(f);
boolean fact = MatchToGoal(f);
if (fact == true) f = facts.size()-1;
if (fact == false) // celu nie ma w faktach
{ // przeszukiwane są konkluzje reguł
for ( int r=0 ; r < rules.size(); r++)
{
boolean rules = SearchConclusionOfRules(r);
if (rules == true)
}
{
}
boolean fire_result = FireingRule(r);
if ((fact == false) && (rules==false))
{
}
boolean ask = AskForAttrValue(atrybut, wartość);
if ((fact==true)||(fire_result==true)||(ask==true))
{
}
else
}
{
}
return true;
return false;
boolean MatchToGoal(int f)
{
}
if ((fakt.attr == a)&&(fakt.valNo == w))
{
}
return true;
boolean FiringRule(int r)
{
boolean maska = 1;
//jeśli chodź jedna przesłanka nie będzie prawdziwa
...
...
to zmienna maska będzie równa 0 i funkcja FiringRule
zwróci wartość false
for (int w = 0 ; w
24
zwala na uogólnianie reguł w bazach wiedzy poprzez
zastępowanie konkretnych wartości atrybutów bądź
to w konkluzjach reguł bądź w przesłankach reguł
– zmiennymi. Jeśli w przesłance czy konkluzji reguły
występuje zmienna (np. „X”) to w procesie wnioskowania
nastąpi próba podstawienia pod wartość atrybutu
wszystkich możliwych wartości. Wyobraźmy sobie
klienta banku, który chce założyć sobie konto przy
czym nie wie jeszcze jaka opcja będzie dla niego optymalna.
Wówczas system ekspertowy mający wspomóc
go w podjęciu takiej decyzji uruchomi się z hipotezą
np. „opcja konta= X” i w procesie wnioskowania
zostaną podjęte próby sprawdzenia
prawdziwości zarówno hipotezy „opcja
konta= A”, „opcja konta= B” jak i „opcja
konta= C” zakładając ze tylko takie trzy
opcje: „A”, „B” i „C” są możliwe.
Podsumowując, możemy stwierdzić, że:
• Reguła jest spełniona, jeżeli jej przesłanka
jest prawdziwa.
• Przesłanka reguły jest prawdziwa jeżeli
wszystkie jej literały są faktami.
• Reguła spełniona może być uaktywniona
– jej konkluzja jest dopisywana
do bazy faktów.
• Reguł spełnionych może być wiele,
wyborem reguły do uaktywnienia
steruje strategia doboru reguł, najprostsza
wybiera pierwszą w kolejności.
• Reguła uaktywniona bywa zwykle
wyłączana z aktualnie realizowanego
procesu wnioskowania, tak aby
jej konkluzja nie dopisywała się wielokrotnie
do bazy faktów.
A jak to jest w praktyce ?
Załóżmy, że do reprezentacji wiedzy z zakresu
analizy wniosków kredytowych wykorzystane
zostaną następujące atrybuty:
„Kredyt” z wartościami „Tak” lub „Nie”,
„Sytuacja zawodowa” z wartościami:
„Bezrobotny” bądź „Pracujący”, „Staż pracy”
z wartościami: „Brak”, „1do5”, „6do10”
czy „powyzej10”, „Przeznaczenie kredytu”
z wartościami „Komputer” Lub „Samochod”,
„Stan konta” z wartościami „do200”,
„do100”, „do500”, „do1000” czy wreszcie
„do2000”. Mamy jeszcze atrybuty: „Liczba
rat” z wartościami: 12 lub 24, Atrybut
wiek z wartościami „poniżej20”, „21do30”,
„31do40” i „powyżej40” i atrybut „Wielkość
raty” z wartościami: „20”, „30”, „40” i „50”.
Baza zawiera deklarację ośmiu atrybutów
SZTUCZNA INTELIGENCJA
i sześćdziesięciu reguł o różnej liczbie warunków. Schemat
pliku z bazą wiedzy (z regułami i faktami) wygląda
następująco (Listing 1).
Jak widać baza składa się z bloku deklaracji atrybutów
i ich wartości (blok AttrDefBegin) i z bloku deklaracji reguł
(blok RuleDefBegin). Nie ma ograniczeń zarówno co
do liczby reguł jak i warunków w regułach, a także liczby
atrybutów i ich wartości. Warte podkreślenia jest i to,
że jest to typ tzw. szkieletowego systemu ekspertowego,
co oznacza, że zaimplementowany moduł wnioskowania
będzie współpracował z każdą tak zakodowaną bazą
wiedzy. A więc można dla każdej dowolnej dziedziny:
Rysunek 2. Okno wyboru trybu pracy i strategii doboru reguł
Rysunek 3. Okno wyboru celu wnioskowania
4/2011
Dlaczego maszyna potrafi zachować się jak istota inteligentna ?
biuro podróży czy sklep z prezentami do wyboru stworzyć
bazę wiedzy w takim formacie i uruchomić proces
wnioskowania dla dowolnej hipotezy. Zaimplementowany
został wieloplatformowy moduł wnioskowania wstecz
w środowisku Java (co pozwoli uruchomić system wszędzie,
niezależnie od platformy sprzętowej i programowej),
który dla zadanej hipotezy (uwzględniając
także realizację nawrotów i różne strategie
doboru reguł) przeprowadza proces wnioskowania
zarówno z pracą ciągłą jak i krokową.
Najważniejszymi funkcjami zaimplementowanymi
w systemie zgodnymi z założeniami systemów
ekspertowych są:
1. runInference(atrybut, wartość) – funkcja
przeprowadza proces dowodzenia
poprawności celu wnioskowania hipotezy
podanej jako parametry funkcji.
2. facts.get(f) – metoda klasy kontenerowej
LinkedList(), którą jest m.in. lista
faktów. Metoda get() pobiera kolejny
element listy.
3. MatchToGoal(f) – funkcja próbująca dopasować
analizowany fakt do celu wnioskowania.
Zwraca wartość true w sytuacji
gdy porównywane wartości są identyczne.
W przeciwnym przypadku zwraca
wartość false.
4. SearchConclusionOfRules(r) – funkcja
sprawdza czy konkluzja analizowanej reguły
daje się zunifikować z celem wnioskowania.
Zwraca wartość true w przypadku
sukcesu, false w przypadku porażki.
5. FiringRule(r) – funkcja odpala daną regułę,
czyli dla każdego z jej warunków
uruchamiany jest proces wnioskowania.
Funkcja zwraca wartość true gdy dowodzenie
poprawności dla każdej przesłanki
zakończyło się sukcesem. Wartość
false zwracana jest jako rezultat
funkcji wówczas, gdy co najmniej jedna
przesłanka nie będzie prawdziwa.
6. AskForAttrValue(atrybut, wartość)
– funkcja pyta użytkownika o wartość
analizowanego atrybutu, będącego warunkiem
uaktywnionej reguły. Odpowiedź
użytkownika dodawana jest jako
nowy fakt do listy faktów.
7. showWhyExplanations() – funkcja objaśnia
użytkownikowi powód zapytania go
o wartość danego atrybutu i jak podana
przez niego odpowiedź wpłynie na drogę
rozumowania prowadzonego przez maszynę
wnioskującą.
8. showInferenceResults() – funkcja ma na celu
przedstawić dokładną drogę wnioskowania prowadzoną
przez maszynę wnioskującą.
Po wybraniu celu wnioskowania uruchamiany jest proces
dowodzenia jego poprawności metodą wnioskowa-
Rysunek 4. Okno odpowiedzi na pytanie 1 o prawdziwość przesłanki
Rysunek 5. Okno wyjaśnienia systemu o pytanie do użytkownika
Rysunek 6. Okno decyzji systemu
www.sdjournal.org 25
26
nia wstecz. Realizuje to funkcja RunInference(atrybut,
wartość);, której fragment przedstawiony został w formie
pseudokodu (Listing 2).
Rysunek 8. Okno objaśnienia drogi wnioskowania
Rysunek 7. Okno wyników wnioskowania
SZTUCZNA INTELIGENCJA
Funkcja RunInference() zwraca wartość całkowitą,
czyli gdy rezultat = 1 to znaczy ze wszystkie przesłanki
były prawdziwe a jeżeli 0 tzn., że co najmniej jedna
przesłanka nie została potwierdzona.
Przykładowy
scenariusz programu
- Wirtualny doradca
kredytowy
Załóżmy, że jesteśmy klientem
pracującym, osiągającym
dochody o wartości
do1000, chcącym kupić samochód
i zainteresowanym
wzięciem kredytu na 2 lata.
Główne okno programu wygląda
następująco (patrz Rysunek
1).
Po wybraniu trybu pracy
systemu i strategii doboru reguł
(Rysunek 2) oraz ustaleniu
celu wnioskowania (kredyt=Tak)
(Rysunek 3) rozpoczyna
się proces wnioskowania
prawdziwości tej hipotezy
na podstawie reguł zawartych
w bazie wiedzy i faktów
generowanych na bieżąco
w trakcie pracy systemu.
Jeśli założymy że ustalonym
celem wnioskowania
stała się hipoteza „kredyt =
tak” to system zgodnie z założeniami
metody wnioskowania
wstecz szukał będzie
(w przypadku nie potwierdzenia
tego celu w faktach
w bazie wiedzy) reguł, których
konkluzja potwierdzi taki
cel. Okazuje się, że pierwszą
regułą na liście o konkluzji
„kredyt=tak” będzie reguła:
Kredyt=Tak if Przeznaczenie
kredytu=Samochod;Stan
konta=do1000;Liczba rat=24
Jak widać reguła ma trzy
warunki zatem system rekurencyjne
wywoła algorytm
wnioskowania wstecz
dla każdego warunku po-
4/2011
Reklama
Dlaczego maszyna potrafi zachować się jak istota inteligentna ?
wyższej reguły. Najpierw więc będzie szukał potwierdzenia
dla przesłanki „Przeznaczenie kredytu=samochód”
następnie dla przesłanki „Stan kont= do1000”
i ostatecznie przesłanki „Liczba rat=24”. Okno z pytaniem
o pierwszą przesłankę przedstawia rysunek 4.
Pamiętajmy, że na każdym etapie działania systemu
możemy zapytać system dlaczego potrzebne jest
potwierdzenie danej przesłanki (przykładem jest rysunek
5).
Zakładając, że użytkownik w trakcie dialogu potwierdzi
prawdziwość tych przesłanek proces wnioskowania
zakończy się sukcesem rysunek 5).
Następnie użytkownik ma prawo wglądu w wyniki
wnioskowania: statystyki dotyczące ustawień procesu
wnioskowania, liczby reguł, które zostały użyte we
wnioskowaniu, liczby nowych faktów itp. (rysunek 7).
Jedną z istotnych zalet systemów ekspertowych jest
objaśnienie drogi wnioskowania, dzięki czemu użytkownik
ma możliwość weryfikacji jakie reguły były kolejno
analizowane i uaktywniane i jakie nowe fakty były
w ten sposób generowane (rysunek 8).
Oczywiście fakty tak wygenerowane zostają zapisane
w bazie wiedzy i przy kolejnym uruchomieniu systemu
przez tego samego użytkownika będzie on musiał
zdecydować o tym, czy uruchomić proces wnio-
skowania z faktami z poprzedniego wnioskowania,
czy uprzednio chce usunąć zapisane tam fakty. Jak
się można spodziewać, jeśli użytkownik nie usunie
faktów potwierdzających hipotezę „kredyt=tak” to przy
kolejnym uruchomieniu systemu z takim samym celem
wnioskowania proces zakończy się sukcesem bez zadawania
jakichkolwiek pytań użytkownikowi (hipoteza
zostanie od razu potwierdzona, gdyż cała niezbędna
wiedza jest już zgromadzona w bazie wiedzy).
Warto na końcu podkreślić fakt prostoty budowy tego
typu systemów. Jeśli więc mogą one pozwolić zwiększyć
efektywność pracy w dowolnej dziedzinie, i efektywnie
wspomagać człowieka powinniśmy je tworzyć
i wykorzystywać.
W razie jakichkolwiek pytań i wątpliwości proszę
o kontakt: agnieszka.nowak@us.edu.pl
DR AGNIESZKA NOWAK – BRZEZIŃSKA
dr Agnieszka Nowak - Brzezińska adiunkt w Instytucie Informatyki,
Zakład Systemów Informatycznych Uniwersytetu
Śląskiego w Katowicach. Zajmuje się eksploracją wiedzy,
elementami sztucznej inteligencji a także systemami wyszukiwania
informacji. Brała udział w projekcie badawczym nr
N206153133 pt: ‘’Złożone bazy wiedzy: struktura i procesy
wnioskowania’’, �nansowanym przez MNiSW.
28
Historia sztucznych sieci neuronowych będących
dziedziną nauk technicznych sięga roku
1943, w którym została wydana pionierska
praca autorstwa W.S. McCullocha i W. Pitts’a “A logical
calculus of the ideas immanent in nervous activity”.
W pracy przedstawiono pierwszy matematyczny
opis komórki nerwowej z zasugerowaniem potencjalnych
korzyści wynikających z wykorzystania tego
modelu do przetwarzania danych. Źródłem inspiracji
do prac nad stworzeniem modeli neuronów stanowiły
dla badaczy prace i odkrycia z dziedziny neurofizjologii
i bioniki [2].
Najogólniej sztuczna sieć neuronowa to: układ składający
się z wielu komórek liczących połączonych ze
sobą wzajemnie. Ma trzy unikatowe cechy: jest adaptacyjna
(dostosowuje się do zewnętrznych sygnałów),
ma możliwość zmiany połączeń komórek (konfiguracji),
jest krzepka (odporna na uszkodzenia
- nawet gdy część komórek źle liczy
lub jest uszkodzona).
Do głównych zalet istniejących
sztucznych sieci neuronowych zbliżonych
do cech mózgu ludzkiego
zaliczyć można [2]:
SZTUCZNA INTELIGENCJA
Analiza możliwości
wykorzystania sztucznych sieci
neuronowych w psychologii
Wprowadzenie do sieci neuronowych, rozpoznawanie płci
w środowisku pakietu sztucznej inteligencji Sphinx
Sztuczne sieci neuronowe jako jedna z kilku technik zaliczanych do
kategorii „sztuczna inteligencja” mogą być z dużym powodzeniem
wykorzystywane również w takich obszarach nauki jak psychologia.
Sposób działania (uczenie się) sztucznych sieci neuronowych
naśladujący nieco sposób uczenia się człowieka sprawia, że zadania
z gatunku podziału zbiorów na kategorie (klasy) nie stanowią dla tego
narzędzia żadnych problemów, a efekty nauki są doskonałe.
Dowiesz się:
• Wybranych zagadnień z zakresu podstaw sztucznych sieci neuronowych;
• Jak wygląda nauka sieci neuronowych;
• Podstawy obsługi pakietu sztucznej inteligencji Sphinx.
Powinieneś wiedzieć:
• Podstawowe informacje z zakresu sztucznej inteligencji.
• Zdolność nauki sieci (adaptacyjność) na podstawie
przedstawionych przykładów. Analogia do człowieka
uczącego się na podstawie doświadczeń i powtarzania
informacji (np. liter alfabetu) w przeciwieństwie
do sztywno zadanego algorytmu działania
programu komputerowego.
• Zdolność uogólniania (samoorganizacji) przez
sieć neuronową zjawisk (klasyfikacji) na podstawie
przedstawianych w trakcie nauki przykładów. Analogia
do kojarzenia przez człowieka faktów np. podział
pewnych obiektów, zjawisk na zbliżone do siebie
grupy.
• Zdolność do interpretacji zjawisk i zależności o których
informacje są niekompletne lub zawierają błędy.
Częstym zjawiskiem jest niedobór informacji lub
jego niecałkowita wiarygodność. Człowiek potrafi
poprawnie wysnuć wnioski w takich sytuacjach,
Rysunek 1. Sieć neuronowa wielowarstwowa jednokierunkowa.
4/2011
Analiza możliwości wykorzystania sztucznych sieci neuronowych w psychologii
sieć neuronowa w ograniczonym zakresie również
zdolność tą posiada.
• Równoległość przetwarzania informacji, gwarantująca
bardzo szybkie działanie sieci neuronowej.
Wykorzystanie sieci neuronowych w psychologii jest
ciekawą i oryginalną formą ich wykorzystania. Wyobraźmy
sobie, że mamy zestaw kilkuset odpowiedzi
na ankiety z pytaniami o cechy osobowości człowieka
a sieć neuronowa jest w stanie wskazać np. płeć osoby
ankietowanej, jej potencjał na dane stanowisko w firmie
(pomoc dla działu doboru personelu) lub inną cechę
jako efekt analizy. Oczywiście jest to możliwe tylko
pod warunkiem, że mamy oprócz samych odpowiedzi
na ankietę również podaną przez ankietowanego badaną
przez nas cechę. Potencjał zdolności do klasyfikacji
sieci neuronowej wykorzystywany jest zarówno przy
wykorzystaniu sieci neuronów uczonych pod nadzorem
(znane są odpowiedzi) jak również w przypadku sieci
�� �
�
�� �
�
�� �
�
�� �
�
�����������������
�����������������������
�� �
�
�� �
�
�� �
�
�� ��
�� ��
�� ��
�
�
�
�
���������
��������
�����������
��������������
������
�
�������������������������
neuronowych uczonych bez nadzoru „samoorganizujących
się” (odpowiedzi nie są znane – następuje na wyjściu
sieci neuronowej podział danych wejściowych na
kategorie). W tym artykule omówione zostaną elementy
sieci uczonej „z nauczycielem”.
Architektura sieci neuronowej
Podstawowa architektura sieci neuronowej wykorzystywanej
do nauki „z nauczycielem” to sieć neuronowa
wielowarstwowa jednokierunkowa z algorytmem uczenia
propagacji wstecznej błędu. To najpopularniejszy
typ sieć i najczęściej wykorzystywany algorytm uczący.
Dodać należy, że istnieje wiele innych algorytmów
uczących możliwych do wykorzystania w sieci neuronowej
wielowarstwowej m.in. gradienty sprzężone, algorytmy
Quasi-Newtona (np. BFGS), Levenberga-Marquardta,
o często większym potencjale (szybkość uczenia,
większa szansa na znalezienie ekstremum globalnego
funkcji celu)
�����������������������
��������������������
Rysunek 2. Przetwarzanie informacji w sieci neuronowej z algorytmem uczenia propagacja wsteczna błędu.
�� ��
�������������� �����������������
�
�������
�
������
�
������
�
�� �
�
������������
��������
���������
www.sdjournal.org 29
�� �
�
�� �
�
�� �
�
�
�
�������
�� �
�
�� �
�
�� �
�
������
�
������
�
������
�
�� �
�
�� �
�
�� �
�
30
Sieci neuronowe jednokierunkowe posiadają zazwyczaj
jedną lub dwie warstwy ukryte (ang. hidden layers),
jedną warstwę wejściową (ang. input layer) i jedną warstwę
wyjściową (ang. output layer). Każdy neuron z danej
warstwy ma jedno wyjście (generuje jeden sygnał)
i połączony jest z każdym z neuronów kolejnej warstwy.
Liczba wyjść sieci neuronowej równa jest liczbie neuronów
w warstwie wyjściowej.
Rysunek 2 przedstawia szczegóły przepływu informacji
w trakcie nauki sieci neuronowej. Przyjęto, że sieć
neuronowa ma N wejść, M neuronów w warstwie ukrytej
oraz P neuronów w warstwie wyjściowej (wyjść).
Wagi w warstwie wyjściowej oznaczono jako v, natomiast
wagi w warstwie ukrytej oznaczono jako w. Wyjścia
warstwy wyjściowej generują sygnały y, natomiast
wyjścia z warstwy ukrytej generują sygnały z.
Wzór na modyfikację wag dla neuronów warstwy wyjściowej
po każdej epoce treningowej czyli po prezentacji
wszystkich K wzorców treningowych (wykorzystuje
się sumaryczny błąd sieci) wygląda następująco [2]:
Jest to metoda raczej rzadko wykorzystywana w praktyce,
raczej ma sens dla bardzo ograniczonego ilościowo
zestawu danych uczących.
Natomiast znacznie częściej wykorzystywany w praktyce
wzór na modyfikację wag dla neuronów warstwy
wyjściowej po każdym pojedynczym wzorcu treningowym
wygląda następująco:
Nauka sieci neuronowej
Rozpatrujemy tutaj uczenie nadzorowane “z nauczycielem”.
W tym typie nauki stosujemy zbiór uczący,
składający się z zestawu danych podawanych na wejście
oraz danych podawanych na wyjście (oczekiwanych).
Wskazujemy sieci w czasie nauki poprawną odpowiedź.
Przykładowo, ucząc sieć tabliczki mnożenia
danymi wejściowymi będą pary liczb (...(2,3), (2,4),
(2,5)...) a wyjściowymi liczby (...6,8,10...) W trakcie na-
Komplet zebranych
danych (zestawy
informacji
wejściowych oraz
wyjściowych)
SZTUCZNA INTELIGENCJA
Dane testowe
(typowo 10% z
kompletu zebranych
danych)
Dane treningowe
(typowo 90% z
kompletu
zebranych danych)
Rysunek 3. Podział zebranych danych przed rozpoczęciem nauki
sieci neuronowej
uki sieć dostraja swoje wagi w ten sposób, aby różnica
wartości pomiędzy oczekiwaną na wyjściu a nauczoną
osiągnęła pewną wartość minimalną.
Aby nauka miała sens i było możliwe zweryfikowanie
jakości nauki należy zawsze całkowity zestaw danych
podzielić na dwa podzbiory – dane treningowe (sieć
neuronowa na nich będzie się uczyła rozwiązania danego
zadania) oraz testowych (na nich nastąpi weryfikacja
efektów nauki sieci neuronowej-tych danych w trakcie
nauki sieć neuronowa nie zna) (rys.3).
Rozpoznawanie płci w środowisku pakietu
sztucznej inteligencji Sphinx
Przed rozpoczęciem zadania należy oczywiście zgromadzić
odpowiednią liczbę wypełnionych ankiet i wprowadzić
dane do arkusza kalkulacyjnego podając w jednym
wierszu dane wejściowe (odpowiedzi na ankiety zapisane
w postaci liczby (np. odpowiedz a na dane pytanie
to 1, odpowiedz b to 2 itd., ostatnim elementem wiersza
jest kod płci (np. 1 to mężczyzna odpowiadał na ankietę,
0-kobieta) Pytań powinno być co najmniej 20, natomiast
ankiet im więcej tym lepiej ale przyjąć należy, że
co najmniej kilkaset aby jakość rozpoznawania była zadawalająca.
Około 10% danych z wierszy należy przeznaczyć
do testowania nauczonej sieci neuronowej.
Przykładowe pytania (z testu który opracowałem i wykorzystałem
do rozpoznawania płci, w sumie ankieta
zawierała 26 pytań – oczywiście jedno z pytań było
o płeć):
..........
.........
Pyt. Czy jesteś osobą oszczędną: 1-zdecydowanie tak,
2-raczej tak, 3-raczej nie, 4-zdecydowanie nie
Pyt. Czy uważany jesteś za osobę energiczną: 1-zdecydowanie
tak, 2-raczej tak, 3-raczej nie, 4-zdecydowanie
nie
Pyt.. Czy można na tobie polegać: 1-zdecydowanie tak,
2-raczej tak, 3-raczej nie, 4-zdecydowanie nie
Pyt. Czy jesteś osobą punktualną: 1-zdecydowanie tak,
2-raczej tak, 3-raczej nie, 4-zdecydowanie nie
Pyt. Czy jesteś osobą wesołą i pogodną: 1-zdecydowanie
tak, 2-raczej tak, 3-raczej nie, 4-zdecydowanie nie
Pyt. Czy pracując bądź też ucząc się wolisz to robić we
współpracy z innymi: 1-zdecydowanie tak, 2-raczej tak,
3-raczej nie, 4-zdecydowanie nie
Rysunek 4. Menu początkowe programu Neuronix.
4/2011
Analiza możliwości wykorzystania sztucznych sieci neuronowych w psychologii
Rysunek 5. Okno kreatora nowego projektu.
Pyt. Czy jesteś osobą gadatliwą: 1-zdecydowanie tak,
2-raczej tak, 3-raczej nie, 4-zdecydowanie nie
......
......
Zadanie rozpoznawania płci wykonane zostanie w programie
symulującym sieci neuronowe – Neuronix, który
jest modułem profesjonalnego pakietu sztucznej inteligencji
Sphinx firmy AITECH [3], mogącym łączyć
moduł sieci neuronowych z modułem systemów ekspertowych.
Program Zawiera ponadto język programowania
pozwalający stworzyć własny interfejs użytkownika
do zadanego problemu. Pakiet sztucznej inteligencji
rozwijany jest od 1990 roku przez firmę Aitech
Artificial Intelligence Laboratory. W skład pakietu
wchodzą następujące elementy [3]:
• system PC-Shell - szkieletowy system ekspertowy,
• system Neuronix - symulator sieci neuronowej,
• system CAKE - system komputerowego wspomagania
inżynierii wiedzy,
• system HybRex - system do budowy inteligentnych
aplikacji,
• system Predyktor - system prognostyczny,
• system deTreex - indukcyjny system pozyskiwania
wiedzy,
• system demoViewer - system do prezentacji aplikacji
pakietu AitechSPHINX.
Pakiet wyposażony jest we własny język reprezentacji
oraz translator do niego. Ma charakter narzędziowy, zatem
nie jest zorientowany dziedzinowo. System PC-Shell
jest systemem hybrydowym, zawierającym elementy architektury
tablicowej (ang. blackboard systems). Obecnie
jest wykorzystywany w dydaktyce i pracach badawczych
na polskich uczelniach oraz w firmach komercyjnych, między
innymi w zakładach energetycznych oraz bankach.
W programie Neuronix możemy korzystać z sieci jednowarstwowych
jednokierunkowych oraz sieci wielo-
warstwowych jednokierunkowych.
Jako funkcję aktywacji przyjęto funkcję
sigmoidalną unipolarną.
W programie mamy możliwość
określenia takich parametrów jak [1]:
współczynnik uczenia, współczynnik
momentu, maksymalna wartość wag,
rozmiar cyklu uczącego, mieszanie
wzorców, dodatkowy neuron „Bias”
oraz tolerancję. Wartość współczynnika
uczenia zawiera się w przedziale
. Im większa jego wartość, tym
szybsze przeszukiwanie przestrzeni
rozwiązań. Jednak zbyt duża wartośc
może doprowadzić do przeoczenia
najlepszego rozwiązania. Współczynnik
momentum zawiera się w przedziale
32
jest równe 1. Zastosowanie go powoduje zwiększenie
stabilności w trakcie uczenia i jest klasycznym przykładem
na poprawienie osiągów sieci. Tolerancja służy do
określenia dopuszczalnego błędu na pojedynczym wyjściu
sieci. Wartość tolerancji zawiera się w przedziale
od 0 do 1, co jest następstwem faktu, że w symulatorze
Neuronix stosuje się logistyczną funkcję przejścia w postaci
funkcji sigmoidalnej unipolarnej.
System Neuronix pozwala na śledzenie wskaźników
dających wgląd w bieżący stan sieci podczas procesu
uczenia. Uczenie może odbywać się również w trybie
pracy krokowej. Możliwe jest także automatyczne zapisywanie
wag w trakcie uczenia. W programie stosowane
są dwa wskaźniki służące szacowaniu jakości wiedzy
zdobytej przez sieć na etapie uczenia: błąd RMS
oraz ilość wzorców poza tolerancją. Należy oczekiwać,
że w trakcie uczenia wartość błędu RMS będzie malała
w miarę kolejnych epok uczących, choć drobne wahania
są dopuszczalne.
Ilość wzorców poza tolerancją to parametr, który, mówi
o ilości wzorców nie mieszczących się w bieżącej
wartości tolerancji dla wyjść sieci.
Warunkami niezbędnymi do uruchomienia uczenia
są: przygotowanie pliku uczącego, oraz dodatkowo należy
wskazać plik wag, do którego zapisywane będą aktualne
wagi sieci i ustawić wartości parametrów, jeżeli
mają być inne niż domyślne.
Mając już dane podzielone na dane treningowe służące
do treningu sieci neuronowej (np. plik pl_tre.xls) oraz
dane testowe służące do testowania jakości nauczonej
sieci neuronowej (np. plik pl_test.xls).
Po stworzeniu obu plików można przejść do etapu
badań przy użyciu sieci neuronowej. ruchamiamy program
Neuronix z pakietu sztucznej inteligencji Sphinx.
Rysunek 8. Okno Parametry generacji plików
SZTUCZNA INTELIGENCJA
Wybieramy Menu Plik, pozycja Nowy projekt, pozycja
Kreator nowego projektu.
Nadajemy nazwę projektowi i zapisujemy pusty projekt
wskazując miejsce na dysku do którego mamy prawa
zapisu. Pojawi się żółte okno kreatora projektu.
Klikamy 5 razy w przycisk „>” aż pojawi się okno
z wewnętrznym arkuszem do wprowadzania danych.
Do komórek w wierszu z danymi (białe komórki) wklejamy
zawartość pliku pl_tre.xls pomijając 2 górne wiersze.
Do części szarej wprowadzamy etykiety danych
czyli 2 górne wiersze z pliku pl_tre.xls. Należy skopiować
2 górne wiersze z pliku pl_tre.xls a następnie
wrócić do programu Neuronix i po naciśnięciu prawego
przycisku myszy (będąc w obszarze białej części
arkusz) wybieramy z menu pozycję edycja nagłówka.
Wklejamy skopiowane dane do szarej części nagłówkowej
arkusza programu Neuronix.
Klikamy jeden raz w przycisk „>”. Pojawi się okno Parametry
generacji plików. Zatwierdzamy sugerowane
parametry klikając OK.
Zwróćmy uwagę, że program samodzielnie wylosuje
10% danych wprowadzonych do arkusza i będzie je
wykorzystywał tylko to testowania jakości sieci neuronowej
w trakcie treningu (są to dane testowe wykorzystywane
w trakcie nauki aby kontrolować czy proces nauki nie
uległ tzw. przeuczeniu-wtedy odpowiedzi na danych te-
Rysunek 10. Okno Opcje sieci neuronowej – zakładka parametry
strukturalne sieci.
Rysunek 11. Zakładka parametry procesu uczenia
4/2011
Reklama
Analiza możliwości wykorzystania sztucznych sieci neuronowych w psychologii
stowych są coraz gorszej jakości pomimo, że na danych
treningowych jakość odpowiedzi może nadal rosnąć) Natomiast
trening sieci neuronowej będzie wykorzystywał
pozostałe 90% danych stanowiących dane treningowe.
Proces nauki należy zakończyć przed momentem
przetrenowania sieci neuronowej.
Przy dwóch pytaniach o nadpisanie plików wybrać
przycisk OK. Nacisnąć przycisk „>” a następnie koniec
w żółtym kreatorze projektu. Wybieramy menu Plik, pozycja
Zapisz aby zapisać dotychczasową pracę. Wybieramy
menu Narzędzia, pozycja Parametry sieci. Pojawi
się okno z 4 zakładkami do podania parametrów.
W zakładce Parametry strukturalne sieci podajemy liczbę
neuronów w poszczególnych warstwach lub jednej
warstwie. Zaznaczamy lub odznaczamy opcję Bias. I tutaj
uwaga praktyczna rozwiązanie można uzyskać dobre
jakościowo dla 1 warstwy ukrytej, tym niemniej mi
osobiście udało się uzyskać lepsze przy wykorzystaniu
2 warstw ukrytych. Liczba neuronów w pojedynczej warstwie
ukrytej powinna być w pewnej korelacji w stosunku
do liczby informacji na wejściu sieci neuronowej. Przykładowo
dla 25 wejść warto rozważyć liczbę neuronów warstwie
ukrytej od 15 do 30. Przy 2 warstwach ukrytych,
druga warstwa ma tyle samo a najczęściej mniej neuro-
Rysunek 12. Okno – Monitoring procesu uczenia
�������������������������������������������������������������������������
nów niż pierwsza warstwa ukryta czyli dla naszego przykładu
np. 15-10 w kolejnych warstwach ukrytych może
być wykorzystane. Zbyt mała liczba neuronów powoduje
brak możliwości prawidłowego rozwiązania zadania,
a zbyt duża jest niekorzystna również z uwagi na skłonności
do nauki na pamięć przez sieć neuronową.
W zakładce Parametry procesu uczenia podajemy
wybrane przez nas wartości. Zatwierdzamy klikając na
Zastosuj.
W zakładce Parametry procesu testowania podajemy
częstość testowania równą 1. W zakładce Warunki zakończenia
procesu uczenia nie wprowadzamy żadnych zmian.
Klikamy OK. aby opuścić okno Opcje sieci neuronowej.
�����������������������������������������������������������������
34
Przechodzimy do fazy nauki sieci neuronowej wybierając
menu Sieć, pozycja Uczenie sieci. W oknie Monitoring
procesu uczenia zaznaczamy Prezentacja: Statystyki
oraz Wykresu RSM aby w trakcie nauki obserwować
dokładniej postępy nauki.
Przed rozpoczęciem nauki inicjalizujemy wagi początkowe
klikając w przycisk Inicjacja wag. Wagi początkowe
uzyskają losowe wartości początkowe. Naukę rozpoczynamy
klikając w przycisk Start.
Obserwujemy uważnie postępy nauki, szczególnie
w oknie TESTOWANE. Należy proces nauki zatrzymać
klikając w przycisk Stop w chwili gdy w oknie TESTO-
WANIE pozycja błąd RMS dość niski, pozycja Poza tolerancją
wskazuje stosunkowo małą liczbę oraz w momencie
zauważenia na wykresie błędów treningowych
i testowych, że błędy testowe zaczynają rosnąć (sieć
neuronowa jest przetrenowana, testowanie sieci daje
coraz gorsze rezultaty) Najlepiej zatrzymać proces nauki
tuż przed tym niekorzystnym zjawiskiem (rys.9). Wagi
zmienione w trakcie nauki zostaną zapisane po naciśnięciu
przycisku zapis wag. Należy koniecznie pamiętać
o zapisie wag. Naukę można powtórzyć od nowa zaczynając
od Inicjacji wag i przycisku Start (jeśli uznamy,
że sieć neuronowa jest zbyt mocno przetrenowana) lub
kontynuować przerwaną naukę przyciskiem Stop poprzez
ponowne naciśnięcie przycisku Start (jeśli uznamy,
że sieć neuronowa została zatrzymana zbyt wcześnie).
Należy zauważyć, że błąd RMS w oknie UCZE-
NIE odnosi się do faktów treningowych i dlatego maleje
bardzo długo. Od pewnego momentu (moment przetrenowania
– błąd RMS w oknie TESTOWANIE rośnie cały
czas) sieć neuronowa zatraca zdolności do uogólniania
aczkolwiek błąd RMS w oknie UCZENIE maleje nadal
sieć neuronowa uczy się niepotrzebnie niemal na pamięć
wzorców treningowych. Po zakończeniu nauki notujemy
liczbę epok treningowych, błąd RMS testowania
oraz liczbę faktów poza tolerancją. Okno Monitoring procesu
uczenia zamykamy klikając na przycisk Koniec. Należy
teraz poddać testowaniu nauczoną sieć neuronową
wykorzystując dane z pliku testowego pl_test.xls. Wybieramy
Menu Sieć, pozycja Uruchomienie sieci. Do otwartego
okna arkusza wklejamy dane z pliku pl_test.xls, pomijając
dwa pierwsze wiersze nagłówkowe oraz kolumnę
ostatnią (wy) – dane wyjściowe dla sieci neuronowej. Klikamy
prawym przyciskiem myszy otwierając menu. Wy-
Literatura
SZTUCZNA INTELIGENCJA
Rysunek 9. Ilustracja efektu przetrenowania sieci neuronowej
Rysunek 13. Nauka sieci neuronowej.
bieramy pozycję Uruchomienie sieci. Ostatnia kolumna
wy została wypełniona odpowiedziami nauczonej sieci
neuronowej na podane dane testowe.
Wyniki w kolumnie wy nie są zerojedynkowe tak jak zakodowano
płeć. Należy więc np. a arkuszu kalkulacyjnym
dane te zaokrąglić do postaci 0 lub 1 w zależności do której
z tych liczb wynik jest bliższy. Mając wyniki w postaci
0 i 1 możemy porównać je z faktycznymi płciami określonymi
w pliku pl_test.xls. A następnie obliczyć procent poprawnie
rozpoznanych płci osób ankietowanych. Możliwe
jest osiągnięcie nawet 90% trafień przy odrobinie wprawy
w optymalnym ustawieniu parametrów sieci neuronowej
i odpowiednio dobranemu czasowi nauki.
W rozpatrywanej analizie wykorzystano 26 danych
wejściowych (pytania z ankiety), 1 daną wyjściową
• [1] Baczyński D., Parol. M., Piotrowski P., Bielecki S, Wasilewski J. „Sztuczna inteligencja w praktyce” – laboratorium, Oficyna
Wydawnicza Politechniki Warszawskiej, 2008
• [2] P. Helt, M. Parol, P. Piotrowski: Metody sztucznej inteligencji w elektroenergetyce. Oficyna Wydawnicza Politechniki Warszawskiej,
Warszawa 2000
• [3] www.aitech.pl/
• [4] Michalik K.: Neuronix 3.0 dla Windows 9x/NT/2000. Symulator sztucznych sieci neuronowych. Podręcznik użytkownika.
AITECH Artificial Intelligence Laboratory, Katowice 2002.
4/2011
Reklama
Analiza możliwości wykorzystania sztucznych sieci neuronowych w psychologii
(płeć), natomiast liczba epok treningowych wynosiła typowo
2000-4000. Liczba ankiet wynosiła 600, z czego
60 stanowiły ankiety testowe.
Wnioski końcowe
Sieć neuronowa skutecznie potrafi rozpoznać płeć na
podstawie analizy pytań o charakterze psychologicznym
(cechy charakteru, upodobania itp.). Rozważyć należy
czy rozpoznawała płeć biologiczną czy raczej płeć mózgu?.
Ja uważam, że raczej to drugie. Stąd też częściowo
może wynikać fakt nierozpoznania około 10% osób
z ankiet testowych, mający swoje źródło zapewne częściowo
w fakcie, że osoby ankietowane podawały płeć
biologiczną. Istnieją testy bazujące na psychologii, które
Rysunek 14. Arkusz z wynikami testowania sieci neuronowej
wskazują płeć mózgu. Test taki jest dostępny np. na stronie:
http://www.piotrowski_pawel.republika.pl/ w dziale
psychologia lub krótsza, prostsza postać testu na http:
//adonai.pl/relaks/psychotesty/?id=1.
DR INŻ. PAWEŁ PIOTROWSKI
Absolwent Wydziału Elektrycznego Politechniki Warszawskiej. Od maja 1995r. adiunkt w Zakładzie Sieci i Systemów
Elektroenergetycznych w Instytucie Elektroenergetyki Politechniki Warszawskiej. W pracy naukowej i dydaktycznej
zajmuje się m.in.: metodami sztucznej inteligencji do zastosowań w elektroenergetyce (prognozowanie, optymalizacja),
aspektami elektrycznymi sieci komputerowych (zasilanie gwarantowane, niezawodność, systemy okablowania
strukturalnego, technologia PoE, technologia PLC), analizą i projektowaniem systemów informatycznych, sieciami
komputerowymi, technologiami internetowymi oraz systemami operacyjnymi komputerów. Jego praca doktorska pt: „Optymalizacja
regulacji napięć w elektroenergetycznych sieciach rozdzielczych w oparciu o teorię sieci neuronowych” wygrała ogólnopolski konkurs
o Nagrodę Promocyjną Siemensa. Autor lub współautor 43 publikacji naukowo-dydaktycznych, 26 referatów na konferencje naukowe
oraz 42 opracowań naukowych. Hobby: psychologia, fotogra�a, poezja, kulturystyka, jogging, modelarstwo. Kontakt z autorem:
pawel.piotrowski@ien.pw.edu.pl. Strona prywatna autora: http://www.piotrowski_pawel.republika.pl/
36
Przeznaczeniem biblioteki graficznej OpenGL
jest generowanie grafiki, a w szczególności grafiki
trójwymiarowej. Biblioteka
ta nie wspiera wprost projektowania
interfejsu użytkownika, ani
zarządzania oknami czy też obsługi
komunikatów systemu operacyjnego.
Projektując aplikację
pod Windows z użyciem tej biblioteki
musimy sami zapewnić taką
funkcjonalność naszej aplikacji, co
często zajmuje dużo czasu. Jednak,
gdy możemy sobie pozwolić
na poświęcenie drobnej części wydajności
aplikacji, to do wykorzystania
mamy wszelkie dobrodziejstwa,
jakie daje nam środowisko
Microsoft Visual Studio i aplikacje
korzystające z komponentów Windows
Forms.
Przygotowanie środowiska
Jak w przypadku większości aplikacji
wykorzystujących Windows
PROGRAMOWANIE GRAFIKI
OpenGL i komponenty
Windows Forms
Renderowanie sceny za pomocą funkcji OpenGL w aplikacji
korzystającej z komponentów Windows Form
Projektując aplikacje korzystające z biblioteki graficznej OpenGL
samodzielnie musimy zadbać o prawidłowe rozmieszczenie
i oprogramowanie interfejsu użytkownika. Przy jego projektowaniu
idealnie wspomoże nas wykorzystanie środowiska Microsoft Visual
Studio, które odciąży nas od czasochłonnego zajęcia. W artykule
tym znajdziesz podstawowe informację na temat tego, jak w prosty
sposób połączyć funkcję biblioteki OpenGL z aplikacją korzystającą
z komponentów Windows Forms.
Dowiesz się:
• Jak przygotować aplikację korzystającą z komponentów Windows
Form do współpracy z biblioteką OpenGL.
Rysunek 1. Okno kreatora aplikacji
Powinieneś wiedzieć:
• Podstawy programowania pod Microsoft Windows;
• Podstawy programowania w Microsoft Visual C++;
• Podstawowa znajomość API OpenGL.
Form rozpoczynamy nasz projekt również od standardowego
wywołania kreatora z menu File�New�Project.
4/2011
Rysunek 2. Zakres mody�kacji właściwości projektu
Następnie wybieramy z dostępnych kreatorów Windows
Forms Application (Rysunek 1).
Gdy kreator zakończy swoje działanie i na ekranie
pokaże się pierwsze okno naszej aplikacji musimy
wskazać linkerowi, że będziemy korzystać z zewnętrznych
plików bibliotecznych OpenGL. Wykonujemy to
przez modyfikację domyślnych właściwości projektu
(poprzez okno konfiguracyjne dostępne z menu Project->nazwa_projektu
Properties…). W wyświetlonym
oknie zmieniamy parametr Configuration: z ’Active(Debug)’
na ’All Configurations’, co zapewni nam to, że raz
dokonane zmiany w konfiguracji linkera odniosą skutek
zarówno do trybu Debug jak i Release (Rysunek 2).
Następnie z drzewa właściwości wybieramy gałąź
Configuration Properties->Linker->Input (Rysunek 3)
i modyfikujemy właściwość: Additional Dependencies
Rysunek 3. Okno właściwości projektu
OpenGL i komponenty Windows Forms
Rysunek 4. Okno dodatkowych zależności linkera
dopisując bibliotekę opengl32.lib oraz glu32.lib (Rysunek
4).
W taki sposób przygotowaliśmy nasz projekt do
współpracy z biblioteką OpenGL i możemy przejść do
kolejnych, ciekawszych czynności.
www.sdjournal.org 37
38
Przygotowanie interfejsu aplikacji
Najprostszym sposobem powiązania OpenGL z oknami
Windows jest użycie jednej z bibliotek tj. AUX,
GLUT lub freeGLUT, które dodatkowo zapewniają
przenoszalność kodu OpenGL pomiędzy różnymi systemami
operacyjnymi. Jednak w naszym przypadku
ta funkcjonalność nie jest istotna, dlatego skorzystamy
z interfejsu pomiędzy Windows i OpenGL, jakim są
funkcje WIGGLE.
Poprawne zainicjowanie okna do renderingu wymaga
określenia kontekstu urządzenia tego okna, dobrania
formatu piksela oraz utworzenia na tej podstawie
kontekstu renderowania OpenGL. Jak wiemy, każdy
komponent w systemie Windows jest zdefiniowany
jako okno. Dzięki takiemu rozwiązaniu proces renderowania
nie musi być ograniczony tylko do głównego
okna aplikacji, ale może również odbyć się na takich
komponentach jak Panel, Button, TextBox, a nawet
Label. Uogólniając, funkcje OpenGL mogą renderować
sceny z użyciem dowolnego komponentu pod
warunkiem, że możliwe jest pobranie uchwytu do okna
(również poprzez jawną konwersję typów) tego komponentu.
Celem naszej przykładowej aplikacji będzie wyświetlenie
dwóch niezależnych scen w jednym oknie. Dodajmy
z paska narzędziowego dwa panele (Toolbox-
>Containers->Panel), na których będziemy wyświetlać
sceny. Dodajmy również stoper (Toolbox->Components->Timer),
który będzie odrysowywał sceny, co
określony czas oraz przycisk (Toolbox->Common Controls->Button),
którym będziemy uruchamiać i zatrzy-
Rysunek 5. Przygotowanie interfejsu aplikacji
PROGRAMOWANIE GRAFIKI
Listing 1. Ostrzeżenie przed zamknięciem aplikacji
System::Void Form1_FormClosing(
{
}
System::Object^ sender,
System::Windows::Forms::FormClosingEventArgs^ e)
if (MessageBox::Show(
{
}
"Czy na pewno chcesz opuścić program?",
"Koniec pracy",
MessageBoxButtons::YesNo,
MessageBoxIcon::Question,
MessageBoxDefaultButton::Button2)
== Windows::Forms::DialogResult::No)
// odwołujemy zamknięcie aplikacji,
// gdy użytkownik naciśnie przycisk 'Nie'
e->Cancel = true;
Listing 2. Wywołanie polecenia zakończenia aplikacji z menu
programu
System::Void wyjścieToolStripMenuItem_Click(
{
}
System::Object^ sender,
System::EventArgs^ e)
// zamykamy okno programu
this->Close();
4/2011
���������������������������������������
��������������������� �����������������
Zapraszamy na konferencję, na której dowiesz się:
jakie są najczęściej popełniane błędy w kodzie aplikacji,
jak tworzyć bezpieczne oprogramowanie - najlepsze praktyki,
jak zabezpieczyć otoczenie aplikacji webowej,
jak chronić się przed zagrożeniami wewnętrznymi,
jak szacować ryzyko i planować nakłady na bezpieczeństwo w projekcie informatycznym.
Wykłady i spotkania
z najlepszymi specjalistami w branży.
Trzy punkty widzenia na bezpieczeństwo aplikacji internetowych:
dewelopera, administratora oraz kierownika projektu.
Warszawa, piątek 13 maja 2011 r.
Zapisz się już dziś - zniżki przy wczesnej rejestracji.
Rejestracja i więcej informacji: www.sages.com.pl/3p
40
mywać nasz stoper. Dodatkowo dodajmy menu (Toolbox->Menus
& Toolbars->MenuStrip), a w zakładce
Plik umieśćmy polecenie zakończenia aplikacji (Rysunek
5).
Po przygotowaniu interfejsu aplikacji możemy przejść
do jego oprogramowania.
Zdarza się, że programista zapomni o tym, że wypada
zapytać użytkownika czy na pewno chciałby zakończyć
działanie aplikacji. W przypadku gdy zostanie zainicjowana
taka czynność wywoływane jest odpowiednie
zdarzenie (Form::FormClosing). Przejmując jego
obsługę zapytamy użytkownika czy chce zamknąć program
(Listing 1).
Teraz możemy oprogramować zdarzenie wywoływane
po uruchomieniu polecenie zakończenia aplikacji
z jej menu (ToolStripMenuItem::Click). Wywołujemy
tutaj metodę zamykającą okno programu (Listing 2).
Ostatnią czynnością, jaką wykonamy na tym etapie
będzie uruchamianie i zatrzymywanie naszego stopera
poprzez kliknięcie przycisku. Po wystąpieniu zdarzenia
(Button::Click) sprawdzamy czy stoper jest uruchomiony,
jeśli tak to go zatrzymujemy, w przeciwnym
wypadku uruchamiamy go. Dodatkowo w każdym przypadku
zmieniamy odpowiednio opis na naszym przycisku
(Listing 3).
Z tak przygotowanym interfejsem użytkownika
przejdźmy do zaimplementowania klasy renderującej.
Listing 3. Włączanie i wyłączanie stopera
System::Void button1_Click(
{
}
System::Object^ sender,
System::EventArgs^ e)
// sprawdzamy czy stoper jest uruchomiony
if (this->timer1->Enabled)
{
}
else
{
}
// jeśli tak:
// zatrzymujemy stoper
this->timer1->Enabled = false;
// zmieniamy tekst na przycisku
this->button1->Text = "Start";
// jeśli nie:
// uruchamiamy stoper
this->timer1->Enabled = true;
// zmieniamy tekst na przycisku
this->button1->Text = "Stop";
PROGRAMOWANIE GRAFIKI
Klasa renderująca
Dla przejrzystości kodu rozdzielimy część obsługującą
funkcje OpenGL od reszty kodu aplikacji tworząc
klasę o przykładowej nazwie OpenGL_base. Deklaracja
klasy znajdzie się w pliku OpenGL_base.hpp (Listing
4), natomiast definicja jej metod w pliku OpenGL_
base.cpp.
Nasza klasa posiada trzy prywatne pola:
Listing 4. Deklaracja przykładowej klasy obsługującej funkcje
OpenGL
#pragma once
#include // używamy API Windows
#include // używamy API OpenGL
#include // używamy narzędzi OpenGL
class OpenGL_base
{
private:
public:
};
// uchwyt do okna
HWND hWnd;
// uchwyt do kontekstu
// urządzenia okna
HDC hDC;
// uchwyt do kontekstu
// renderowania OpenGL
HGLRC hRC;
// konstruktor
OpenGL_base(int handle);
// destruktor
~OpenGL_base(void);
// metoda przeskalowuje scenę
// do podanych rozmiarów okna
void Resize(int width, int height);
// metoda unieważnia zawartość okna
void Invalidate(void);
// metoda inicjująca podstawowe
// parametry sceny
void Initialize(void);
// metody renderujące
void Render_A(float angle);
void Render_B(float angle);
4/2011
Listing 5a. De�nicja konstruktora
OpenGL_base::OpenGL_base(int handle)
: hWnd((HWND) handle), hDC(NULL), hRC(NULL)
{
// pobranie kontekstu urządzenia dla danego okna
this->hDC = GetDC(this->hWnd);
if (!this->hDC)
{
}
MessageBox(NULL,
OpenGL i komponenty Windows Forms
"Nie można ustawić kontekstu urządzenia.",
"Błąd ustawiania DC",
MB_OK | MB_ICONERROR);
this->~OpenGL_base();
exit(EXIT_FAILURE);
// wywołanie funkcji przerzucającej bufory na główny plan
wglSwapLayerBuffers(this->hDC, WGL_SWAP_MAIN_PLANE);
// wypełnienie struktury formatu piksela
static PIXELFORMATDESCRIPTOR pfd =
{
};
sizeof(PIXELFORMATDESCRIPTOR), // rozmiar struktury
1, // wersja struktury
PFD_DRAW_TO_WINDOW | // rysowanie na oknie
PFD_SUPPORT_OPENGL | // obsługa wywołań OpenGL w tym oknie
PFD_DOUBLEBUFFER, // tryb podwójnego buforowania
PFD_TYPE_RGBA, // tryb kolorów RGBA
16, // 16-bitowa gamma kolorów
0, 0, 0, 0, 0, 0, // nie używane
0, 0, 0, 0, 0, 0, // nie używane
16, // 16-bitowy bufor głębokości (Z-Buffer)
0, 0, // nie używane
PFD_MAIN_PLANE, // rysowanie na głównym planie
0, 0, 0, 0 // nie używane
// dopasowanie formatu piksela do obsługiwanych formatów
int pixelFormatIndex = ChoosePixelFormat(this->hDC, &pfd);
if (!pixelFormatIndex)
{
}
MessageBox(NULL,
"Nie można dopasować odpowiedniego formatu piksela.",
"Błąd ustawiania PFD",
MB_OK | MB_ICONERROR);
this->~OpenGL_base();
exit(EXIT_FAILURE);
// ustawienie formatu piksela dla danego kontekstu urządzenia
if (!(SetPixelFormat(this->hDC, pixelFormatIndex, &pfd)))
www.sdjournal.org 41
42
• Uchwyt do okna (HWND) – wskazujący okno aplikacji,
na którym funkcje OpenGL będą rysować.
• Uchwyt do kontekstu urządzenia (HDC) – przechowujący
parametry graficzne wskazanego okna.
• Uchwyt do kontekstu renderowania (HGLRC)
– przechowujący atrybuty graficzne okna OpenGL.
Przyjrzyjmy się definicji konstruktora (Listing 5), którego
parametrem (handle) jest wskazanie okna, na
którym będzie odbywać się rendering sceny. Inicjalizacja
obiektu następuje przez określenie kontekstu
urządzenia dla podanego okna (GetDC). Następnie
konieczne jest dobranie (ChoosePixelFormat) i ustawienie
(SetPixelFormat) odpowiednie formatu piksela
obsługiwanego przez danych sprzęt, jako najbliższego
do danych podanych w strukturze(pfd), w któ-
Listing 5b. De�nicja konstruktora
}
{
}
MessageBox(NULL,
PROGRAMOWANIE GRAFIKI
"Nie można ustawić wybranego formatu piksela.",
"Błąd ustawiania PFD",
MB_OK | MB_ICONERROR);
this->~OpenGL_base();
exit(EXIT_FAILURE);
// utworzenie kontekstu renderowania
this->hRC = wglCreateContext(this->hDC);
if (!this->hRC)
{
}
MessageBox(NULL,
"Nie można utworzyć kontekstu renderowania.",
"Błąd ustawiania RC",
MB_OK | MB_ICONERROR);
this->~OpenGL_base();
exit(EXIT_FAILURE);
// ustawienie danego kontekstu renderowania jako bieżącego
if (!wglMakeCurrent(this->hDC, this->hRC))
{
}
MessageBox(NULL,
"Nie można ustawić bieżącego kontekstu renderowania.",
"Błąd ustawiania RC",
MB_OK | MB_ICONERROR);
this->~OpenGL_base();
exit(EXIT_FAILURE);
rej podajemy m.in. takie parametry jak ilość bitów dla
palety kolorów, bufora głębokości (tzw. Z-Buffer) oraz
czy będziemy używać trybu podwójnego buforowania
scen OpenGL. Ostatnią czynnością inicjalizującą jest
utworzenie kontekstu renderowania (wglCreateContext)
i powiązanie go z kontekstem urządzenia danego
okna (wglMakeCurrent).
Wewnątrz konstruktora użyliśmy funkcję wyświetlającą
okno z komunikatem (MessageBox). Używa ona ciągów
znakowych kodowanych w standardzie MBCS
(Multi-Byte Character Set), a nie standardowym Unicode,
co w rezultacie zwróci błędy kompilacji. Możemy
rozwiązać to przynajmniej na dwa sposoby.
Pierwszym rozwiązaniem tego problemu może być
przestawienie we właściwościach projektu domyślnego
kodowania ciągów znakowych (Rysunek 6). Wybiera-
4/2011
my gałąź Configuration Properties->General, a następnie
przełączamy opcję Character Set na ‘Use Multi-Byte
Character Set’.
Drugą metodą jest dopisanie przed każdym użyciem
ciągu znakowego przedrostka ‘L’ (Listing 6), dzięki któremu
konwertujemy go z typu char* do wchar_t*.
Renderowana scena powinna również reagować
na zmiany kształtu i rozmiaru okna z nią powiązanego.
Skalowanie sceny zapewnia to, że gdy rozmiary
okna ulegną zmianie to również obiekty renderowane
na nim zostaną odpowiednio przeskalowane oraz zachowają
swoje proporcje. Przyjrzyjmy się odpowiedniej
metodzie (Listing 7). Jej parametry wejściowe to szerokość
(width) i wysokość (height) danego okna. Na
wstępie następuje sprawdzenie kontekstów i prawidłowości
wymiarów okna, po czym wiążemy kontekst renderowania
z kontekstem urządzenia i czynimy go bieżącym,
wykorzystując do tego celu jedną z funkcji wiggle
(wglMakeCurrent). Następnie ustalamy, że cały obszar
okna może być wykorzystywany przez OpenGL
(glViewort). W kolejnym kroku przełączamy bieżącą
macierz na macierz rzutowania (glMatrixMode), przypisujemy
jej wartości z macierzy jednostkowej (glLoadIdentity)
i włączamy rzutowanie prostokątne (glOr-
Rysunek 6. Zmiana domyślnego kodowania ciągów znakowych
OpenGL i komponenty Windows Forms
Listing 6. Przykład wywołania okna komunikatu bez zmiany
domyślnego kodowania znaków
MessageBox(
tho) ustawiając w ten sposób bryłę obcinania. Na koniec
przełączamy bieżącą macierz na macierz widoku
modelu i jej również przypisujemy wartości z macierzy
jednostkowej.
Będzie nam również potrzebna metoda, która powiadomi
system operacyjny, że zawartość wskazanego
okna jest nieaktualna, a same okno wymaga przerysowania.
Zobaczmy definicję kolejnej metody (Listing 8),
która nam to zapewni wykorzystując funkcję Windows
API (InvalidateRect).
Parametry początkowe sceny mogą zostać zainicjowane
w konstruktorze naszej klasy. Jednak w naszym
przykładzie wykorzystamy do tego celu osobną metodę
(Listing 9), w której możemy określić wszystkie para-
www.sdjournal.org 43
);
NULL,
L"Jakiś komunikat wyświetlany w oknie.",
L"Tytuł tego okna",
MB_OK | MB_ICONERROR
44
metry startowe tj.: kolor wypełnienia sceny, włączenie
efektu mgły czy ustawienie trybu rysowania wielokątów.
Na początku ustawiamy bieżący kontekst renderowania.
Następnie określamy, że renderowane wielokąty
mają być wypełniane tylko od frontu, natomiast z tyłu
renderowane będą tylko ich krawędzie (glPolygon-
Mode). Na koniec ustalamy, że kolorem czyszczącym
scenę (glClearColor) będzie kolor czarny. Zainicjowanie
parametrów sceny poza konstruktorem umożliwia
nam przywrócenie jej domyślnych ustawień w dowolnym
miejscu naszej aplikacji.
Listing 7. Przeskalowanie sceny
void OpenGL_base::Resize(int width, int height)
{
}
// czy istnieją konteksty
if (!this->hDC || !this->hRC)
{
}
return;
// czy rozmiary okna są poprawne
if (width hRC);
// ustawienie widoku na wymiary okna
glViewport(0, 0, width, height);
// wyzerowanie stosu macierzy rzutowania
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// ustawienie bryły obcinania
if (width hWnd, NULL, false);
Listing 9. Inicjalizacja sceny
void OpenGL_base::Initialize(void)
{
}
// ustawienie bieżącego kontekstu renderowania
wglMakeCurrent(this->hDC, this->hRC);
// określenie trybu rysowania wielokątów:
// front: wypełniony
// tył: tylko linie
glPolygonMode(GL_BACK, GL_LINE);
// określenie koloru czyszczenia sceny
// na czarny w trybie RGBA
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
4/2011
46
Listing 10. Renderowanie obracającego się kwadratu
void OpenGL_base::Render_A(float angle)
{
// ustawienie bieżącego kontekstu renderowania
wglMakeCurrent(this->hDC, this->hRC);
}
// czyszczenie sceny kolorem wypełnienia
// oraz czyszczenie bufora głębokości
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_
BIT);
// wyzerowanie układu współrzędnych
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// obrót w osi X
glRotatef(angle, 1.0f, 0.0f, 0.0f);
// przesunięcie w osi X i Y
glTranslatef(-0.25f, -0.25f, 0.0f);
// wejście w tryb rysowania kwadratów
glBegin(GL_QUADS);
// ustawienie koloru rysowania
// na czerwony w RGB
glColor3f(1.0f, 0.0f, 0.0f);
// pozycja wierzchołka 1
glVertex3f(0.0f, 0.0f, 0.0f);
// ustawienie koloru rysowania
// na zielony w RGB
glColor3f(0.0f, 1.0f, 0.0f);
// pozycja wierzchołka 2
glVertex3f(0.5f, 0.0f, 0.0f);
// ustawienie koloru rysowania
// na niebieski w RGB
glColor3f(0.0f, 0.0f, 1.0f);
// pozycja wierzchołka 3
glVertex3f(0.5f, 0.5f, 0.0f);
// ustawienie koloru rysowania
// na żółty w RGB
glColor3f(1.0f, 1.0f, 0.0f);
// pozycja wierzchołka 4
glVertex3f(0.0f, 0.5f, 0.0f);
// wyjście z trybu rysowania
glEnd();
// opróżnieni zawartości kolejki
glFlush();
// zamiana buforów
SwapBuffers(this->hDC);
PROGRAMOWANIE GRAFIKI
Listing 11. Renderowanie obracającego się trójkąta
void OpenGL_base::Render_B(float angle)
{
}
// ustawienie bieżącego kontekstu renderowania
wglMakeCurrent(this->hDC, this->hRC);
// czyszczenie sceny kolorem wypełnienia
// oraz czyszczenie bufora głębokości
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_
BIT);
// wyzerowanie stosu macierzy widoku modelu
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// obrót w osi Y
glRotatef(angle, 0.0f, 1.0f, 0.0f);
// przesunięcie w osi X i Y
glTranslatef(-0.25f, -0.25f, 0.0f);
// wejście w tryb rysowania trójkątów
glBegin(GL_TRIANGLES);
// ustawienie koloru rysowania
// na czerwony w RGB
glColor3f(1.0f, 0.0f, 0.0f);
// pozycja wierzchołka 1
glVertex3f(0.0f, 0.0f, 0.0f);
// ustawienie koloru rysowania
// na zielony w RGB
glColor3f(0.0f, 1.0f, 0.0f);
// pozycja wierzchołka 2
glVertex3f(0.5f, 0.0f, 0.0f);
// ustawienie koloru rysowania
// na niebieski w RGB
glColor3f(0.0f, 0.0f, 1.0f);
// pozycja wierzchołka 3
glVertex3f(0.25f, 0.5f, 0.0f);
// wyjście z trybu rysowania
glEnd();
// opróżnieni zawartości kolejki
glFlush();
// zamiana buforów
SwapBuffers(this->hDC);
4/2011
Musimy również zadbać o poprawne zaalokowanie
pamięci dla obiektów w konstruktorze okna i powiązanie
ich z kolejnymi panelami interfejsu użytkownika (Listing
13).
Pamiętajmy, że przy zamykaniu aplikacji destruktor
okna głównego powinien zwolnić zasoby, zajmowane
również przez renderowane sceny (Listing 14).
Gdy klasa renderująca jest już częścią okna naszej
aplikacji i odpowiednie panele są z nią powiązane, możemy
przystąpić do obsługi zdarzenia przerysowującego
ich zawartość (Panel::Paint). Wewnątrz jego kodu
Listing 12. Mody�kacja deklaracji głównego okna aplikacji.
#pragma once
OpenGL i komponenty Windows Forms
#include "OpenGL_base.hpp" // używamy naszej klasy renderującej
namespace WinForm_OpenGL
{
/* kod wygenerowany przez kreator... */
};
public ref class Form1 : public System::Windows::Forms::Form
{
/* kod wygenerowany przez kreator... */
private:
// deklaracja scen
OpenGL_base* Scene1;
OpenGL_base* Scene2;
// kąt obrotu
float angle;
/* kod wygenerowany przez kreator... */
}
Listing 13. Mody�kacja konstruktora głównego okna aplikacji
Form1(void)
{
/* kod wygenerowany przez kreator... */
}
// utworzenie obiektów scen i powiązanie ich z panelami
this->Scene1 = new OpenGL_base(this->panel1->Handle.ToInt32());
this->Scene2 = new OpenGL_base(this->panel2->Handle.ToInt32());
// inicjalizacja scen
this->Scene1->Initialize();
this->Scene2->Initialize();
// początkowy kąt obrotu
this->angle = 0.0f;
wywołamy odpowiednią metodę renderującą dla danego
panelu (Listing 15).
Analogicznie dla drugiego panelu wywołamy odpowiednio
metodę this->Scene2->Render_B(…).
Następnie przejmijmy obsługę zdarzenia (Panel:
:Resize) wywoływanego przy zmianie rozmiaru lub
kształtu danego panelu (Listing 16).
Podobnie dla drugiego panelu wywołamy odpowiednio
metodę this->Scene2->Resize(…).
Skompilowanie i uruchomienie programu spowoduje
wyświetlenie na panelach nieruchomych figur geo-
www.sdjournal.org 47
48
metrycznych. Natomiast naciskanie przycisku nie uruchomi
ich animacji. Właśnie z tego powodu wprowadziliśmy
stoper do naszej aplikacji. Teraz musimy oprogramować
jedynie zdarzenie (Timer::Tick), które jest wywoływane
co określony czas, aby nasze figury zaczęły
się obracać (Listing 17).
Listing 14. Mody�kacja destruktora głównego okna aplikacji
~Form1()
{
/* kod wygenerowany przez kreator... */
}
// zwalnianie zasobów zajmowanych przez sceny
if (this->Scene1)
{
}
delete this->Scene1;
if (this->Scene2)
{
}
delete this->Scene2;
Rysunek 8. Zmiana trybu dołączania pliku manifestu
PROGRAMOWANIE GRAFIKI
Listing 15. Obsługa zdarzenia przerysowującego panele
System::Void panel1_Paint(
{
}
System::Object^ sender,
System::Windows::Forms::PaintEventArgs^ e)
// wyrenderowanie sceny
this->Scene1->Render_A(this->angle);
Listing 16. Obsługa zdarzenia przeskalowującego scenę
System::Void panel1_Resize(
{
}
System::Object^ sender,
System::EventArgs^ e)
// przeskalowanie sceny
this->Scene1->Resize(
this->panel1->Width,
this->panel1->Height);
4/2011
Rysunek 7. Przykładowa aplikacja
W ten oto sposób zakończyliśmy pisanie kodu źródłowego
naszej przykładowej aplikacji (Rysunek 7),
która wykorzystuje funkcje OpenGL do renderowania
na komponentach Windows Form.
Jeżeli przy kompilacji programu będą pojawiać się
problemy z włączaniem manifestu do pliku wykonywalnego,
to możemy tak ustawić właściwości projek-
Listing 17. Animowanie scen
System::Void timer1_Tick(
{
}
System::Object^ sender,
System::EventArgs^ e)
// zmieniamy wartość kąta
this->angle += 1.0f;
// poinformowanie systemu,
// że powiązane z panelami
// sceny są nie aktualne
// i wymagają przerysowania
this->Scene1->Invalidate();
this->Scene2->Invalidate();
OpenGL i komponenty Windows Forms
tu, aby plik manifestu był generowany osobno poza plikiem
wykonywalnym (Rysunek 8). Zmiany te wykonamy
w gałęzi Configuration Properties->Manifest Tool->Input
and Output i ustawiamy Embed Manifest na
wartość ‘No’.
Podsumowanie
W przedstawionym artykule starałem się przybliżyć
możliwości wykorzystania komponentów Windows
Form do renderowania na nich scen z użyciem biblioteki
OpenGL. Chociaż przykładowe metody renderujące
pozwalają jedynie na wyświetlenie obracających się
prostych figur geometrycznych, to nic nie stoi na przeszkodzie,
aby renderować bardziej skomplikowane
sceny. Jedyne ograniczenia to wyobraźnia programisty
oraz możliwości dostępnej konfiguracji sprzętowej.
Zachęcam do dalszego, samodzielnego eksperymentowania
z przykładowym kodem aplikacji.
KORNELIUSZ WARSZAWSKI
Korneliusz jest doktorantem na Wydziale Elektrotechniki, Informatyki
i Telekomunikacji Uniwersytetu Zielonogórskiego,
gdzie zajmuje się rzeczywistością wirtualną i gra�ką komputerową.
Obecnie pracuje na stanowisku Administratora Systemu
Informatycznego w Sądzie Rejonowym w Nowej Soli.
www.sdjournal.org 49
50
PROGRAMOWANIE GRAFIKI
Stereoskopia.
Implementacja w XNA 4.0
Stereoskopia na dobre zadomowiła się w kinach. Niemal
wszystkie przeboje, począwszy od „Avatara” po filmy animowane
dla dzieci są teraz wyświetlane w wersji 3D. Także wśród urządzeń
kina domowego pojawiają się już urządzenia do prezentacji
obrazu z wrażeniem głębi. Mowa zarówno o telewizorach, jak
i o akcesoriach komputerowych.
Dowiesz się:
• Artykuł przedstawia prostą implementację animowanego
anaglifu. Dzięki niemu zrozumiesz jak tworzone są obrazy dla
dwukolorowych okularów i będziesz mógł użyć tej techniki we
własnych grach tworzonych w XNA.
Widzenie przestrzenne
Możemy widzieć obraz przestrzenny. Wbrew temu,
co zwykło się pisać w artykułach poświęconych kinu
3D, widzenie stereoskopowe (binokularowe) nie
jest tu jedynym, ani nawet najważniejszym mechanizmem.
Do oceny odległości przedmiotów korzystamy
przede wszystkim z perspektywy – im dalej położony
jest przedmiot, tym jest mniejszy. Równie ważne jest
oświetlenie. Bez cieni własnych i rzucanych, wszystko
staje się płaskie, jak na fotografii z oświetleniem typu
high key. I odwrotnie, im bardziej „plastyczne” są cienie,
tym bardziej sugestywna jest głębia. Z obu mechanizmów
co najmniej od czasów Rembrandta korzysta
malarstwo, a współcześnie także grafika komputerowa
3D, do przedstawiania głębi w płaskim obrazie.
A jednak nałożenie okularów 3D i oglądanie obrazów
stereoskopowych daje nowy rodzaj odczuć, który
jest dla widza atrakcyjny. Skąd on się bierze? Odpowiedź
może być tylko jedna – z mózgu. To on jest
odpowiedzialny za przetwarzanie sygnałów docierających
do niego z obu oczu. I to on jest odpowiedzialny
za wrażenie głębi. Jedyne co musimy mu do tego zapewnić,
to dwa obrazy przedstawiające tą samą scenę
widzianą z dwóch, nieco rozsuniętych punktów.
Różnica tych dwóch przedstawień sceny nie jest duża
(rysunek 1), odzwierciedla odległość między lewym
i prawym okiem, która równa jest średnio tylko około
5-7 cm. Mózg łączy te dwa obrazy „kodując” różnice
między nimi jako odległość widzianych przedmio-
Powinieneś wiedzieć:
• Wskazana jest podstawowa znajomość podstaw C#, XNA i
HLSL.
tów od obserwatora. Przedmioty nie mogą być jednak
zbyt odległe. Ten sposób oceny odległości działa bowiem
tylko w najbliższym zakresie odległości, najwyżej
do kilku metrów.
Stereoskopia
Tym jak tworzyć dwa odpowiednio przygotowane obrazy,
i jak je dostarczyć osobno do lewego i prawego
oka zajmuje się stereoskopia. Jest to technika obrazowania,
oddająca wrażenie normalnego widzenia przestrzennego
tzn. takiego, którego efekt zawiera nie tylko
informacje o kształtach i kolorach obiektów, ale także
ich wzajemne zależności przestrzenne (za Wikipedią).
Ludzie interesowali się stereoskopią już w połowie
XIX w. Jedną z najstarszych realizacji tej techniki
polegała na łączeniu przygotowanych obrazów w tak
zwane stereopary. Jednym z pierwszych i najprostszych,
a zarazem najbardziej popularnych stereoskopów
był ten skonstruowany przez Homelsa w 1861 r.
(rysunek 2). Była to bardzo prosta konstrukcja składająca
się z soczewek i uchwytu na obrazy. Mimo to
pozwalała na uzyskanie pożądanego efektu. W miarę
upływu lat, zainteresowanie tego typu rozrywką rosło
i powstawały coraz to nowsze i bardziej skomplikowane
urządzenia.
W roku 1866 Alois Polanecky (1826-1911) zbudował
urządzenie złożone z 25 stereoskopów wbudowanych
w wieloboczną obudowę. Korzystał przy tym z pomocy
francuskiego fotografa i optyka Claude-Marie Ferriera.
4/2011
Rysunek 1. Obraz tej samej sceny widzianej lewym i prawy okiem.
Z tym „Salonem Stereoskopowym” występował na jarmarkach.
W latach osiemdziesiątych XIX wieku berliński
przedsiębiorca August Fuhrmann (1844-1924)
udoskonalił wynalazek Polaneckiego. W ten sposób
powstały pierwsze fotoplastykony (ponownie cytuję
Wikipedię). Pojawiały się one także na terenie obecnej
Polski. Pozwalały one 25 osobom na równoczesne
oglądanie przezroczy stereoskopowych. Potem zainteresowanie
tą technologią zmalało. Na nowo z pełną
mocą wróciło dopiero w ostatnich kilku latach pod postacią
kina 3D (zob. Ramka).
Okulary 3D
Zadaniem okularów 3D jest dostarczenie do obu oczu
dwóch nieco różnych obrazów. Jest kilka metod uzyskania
tego efektu: od okularów czynnych, które naprzemiennie
zaciemniają jeden z okularów (np. okulary
NVIDII), poprzez okulary wykorzystujące zjawisko
polaryzacji światła, do okularów dwukolorowych.
My zajmiemy się tylko tym ostatnim sposobem, najprostszym
i najtańszym w realizacji. Klasyczne okulary
dwukolorowe miały szkła czerwone i niebieskie. Obraz,
który był przez nie oglądany musiał być wobec te-
Stereoskopia. Implementacja w XNA 4.0
go złożeniem dwóch rysunków: niebieskiego i czerwonego.
Lewe oko patrzące przez czerwony okular nie
będzie widziało czerwieni, która zleje się z kolorem
tła nadanym przez szło całemu lewemu obrazowi. Lewe
oko będzie wobec tego postrzegało czerwień jako
swoistą „biel”. Analogicznie prawe oko nie będzie
reagować na niebieski. Pozostałe kolory postrzegane
będą przez lewe i prawe oko jako różne odcienie czerwonego
i niebieskiego. Ze względu na użyte filtry, kolor
zielony postrzegany będzie jako czarny. W efekcie
okulary czerwono-niebieskie nie umożliwiają zobaczenia
obrazu kolorowego. Aby uniknąć tego problemu
obecnie stosuje się raczej szkła czerwone i cyjan (niebieskozielone).
Kolory te dopełniają się (ich suma daje
biel), a jednocześnie są ortogonalne w przestrzeni kolorów.
Jeżeli korzystając z takich okularów spojrzymy
na obraz, na którym rysunek przeznaczony dla lewego
oka narysowany będzie w czerwieni i nałożony na cyjanowy
rysunek dla prawego oka, jak na rysunku 3 (lewy),
powinniśmy zobaczyć obraz 3D z wrażeniem głębi.
Dla porównania rysunek 3 (środkowy) przedstawia
tę samą scenę przygotowaną dla okularów ze szkłami
czerwonym i niebieskim.
Stereoskopia w kinach
W większości kin w Polsce stosowana jest technologia Dolby 3D Digital Cinema. System ten pozwala uzyskać klarowny obraz 3D
ze stosunkowo realistycznym odwzorowaniem barw. Opiera się na podziale pasma barw na części i kierowaniu do lewego i prawego
oka pasm o nieco innym odcieniu. System ten nie wymaga specjalnych ekranów kinowych. Co więcej może być zainstalowany
wszędzie tam, gdzie znajduje się już projektor cyfrowy spełniający wymagania DCI. Domontowuje się do niego tylko kilka
podzespołów. Opiera się na technologii "wavelength triplet" opracowanej przez niemiecką firmę In�tec. Filtr krążkowy wielkości
płyty CD umieszczony wewnątrz cyfrowego projektora, obracając się z dużą prędkością (jednak w sposób zsynchronizowany
z wyświetlaniem klatek - połowa krążka zawsze wyświetla się synchronicznie z klatką przeznaczoną dla lewego oka, druga
połowa odpowiednio zsynchronizowana jest z klatką przeznaczoną dla prawego oka) dzieli widzialne pasmo świetlne na "części"
o różnym odcieniu barw. Dzięki okularom, które otrzymujemy przed seansem są one kierowane do odpowiednich oczu widza.
Filtr może być automatycznie opuszczany i podnoszony, co pozwala w prosty sposób przełączać się między 3D i 2D w zależności
od rodzaju projekcji. Ze względy na łatwość instalacji technologia staje się coraz bardziej popularna.
Informacje zaczerpnięte z http://www.heliosnet.pl/12,Katowice/Kino3D/technologia/
www.sdjournal.org 51
52
Najbardziej zaawansowaną technologią wśród okularów
dwukolorowych jest opatentowana w 2000 r.
technologia ColorCode 3D. Pozwala na oglądanie obrazów
trójwymiarowych w stosunkowo pełnym kolorze.
Przede wszystkim dotyczy to kolorów czerwonego
i zielonego. Okulary tego typu są wyposażone w szkło
koloru bursztynowego (amber) dla lewego oka, którego
zadaniem jest przekazanie jak największej informacji
o kolorze obrazu. Prawy okular ma szło koloru
niebieskiego. Tym kanałem przekazywana jest monochromatyczna
informacje potrzebna do uzyskania
efektu głębi. Obraz przygotowany do tego typu okularów
widoczny jest na rysunku 3, prawy.
Stereoskopia a grafika 3D
Oczywiście aby uzyskać wrażenie głębi, musimy znać
odległości przedmiotów widocznych na scenie od wirtualnej
kamery. Ale to jest informacja, która w grafice
3D jest łatwa do uzyskania. Nawet po przemnożeniu
współrzędnych modeli przez macierz rzutowania
(po tej operacji położenie przedmiotów wyrażane
jest we współrzędnych odpowiadających pikselom na
ekranie), informacja o głębi jest nadal przechowywana.
Potrzebna jest, aby możliwe było przeprowadzenie
testu głębi – ostatniego etapu w potoku renderowania
(zob. ramka „Potok renderowania”).
Na stronie http://www.fizyka.umk.pl/~jacek/
dydaktyka/3d/stereoskopia.zip jest do pobrania projekt,
w którym na scenie rysowana jest bryła złożona
z 27 sześcianów. Z bryły tej możemy wyjąć niektóre
sześciany używając klawiszy F1 – F6. Projekt przygotowany
został w Visual Studio 2010 dla platformy
XNA 4.0. Do renderowania korzystam ze standardo-
Listing 1. Pary wielkości charakteryzujących obie kamery
Matrix viewLeft = Matrix.Identity;
Matrix viewRight = Matrix.Identity;
RenderTarget2D renderTargetLeft;
RenderTarget2D renderTargetRight;
Vector3 filterLeft = new Vector3(1, 0, 0);
Vector3 filterRight = new Vector3(0, 1, 1);
bool cameraEnabledLeft = true;
bool cameraEnabledRight = true;
bool showSeparateViews = true;
PROGRAMOWANIE GRAFIKI
readonly Vector3 cameraDefaultPositionLeft = new Vector3(0.92f, 1f, 4.5f);
readonly Vector3 cameraDefaultPositionRight = new Vector3(1.08f, 1f, 4.5f);
Vector3 cameraPositionLeft;
Vector3 cameraPositionRight;
wego efektu BasicEffect implementującego oświetlenie
Phonga i teksturowanie. Macierz rzutowania dodaje
do obrazu perspektywę. Widoczna na ekranie bryła
obraca się, co możemy wyłączyć naciskając klawisz 0.
Wówczas możliwe jest samodzielne obracanie nią za
pomocą klawiszy kierunkowych (strzałek).
Tego projektu użyjemy jako punktu wyjście dla przygotowania
animowanego anaglifu. Założenie poniższej
implementacji jest takie, że chcemy uniknąć ingerencji
w sposób rysowania sceny, a tym bardziej
w definicje obiektów opisujących przedstawioną bryłę.
Naszym celem jest przygotowanie kodu działającego
z dowolną wcześniej przygotowaną sceną i modelami,
a także niezależnego od użytych do samego renderowania
efektów HLSL. Tak, aby można było użyć
poniższych rozwiązań do dowolnego projektu przygotowanego
przez Czytelnika. To skazuje nas na potrójne
renderowanie: raz dla lewego oka, raz dla prawego
i wreszcie złożenie uzyskanych w ten sposób obrazów
i ich narysowanie na ekranie, niejako w fazie
post-processingu. Będziemy się jednocześnie starali,
aby przedstawiony kod był jak najprostszy i łatwy do
zrozumienia.
Aby uzyskać dwa obrazy dla lewego i prawego oka,
musimy umieścić na scenie dwie kamery (dwie osobne
macierze widoku). Odległość, w jakiej powinny znajdować
się kamery względem siebie powinna być równa
1/30 odległości od najbliższego obiektu na scenie. Jednak
jest to wartość czysto teoretyczna, od której warto
zacząć własne eksperymenty. W trakcie implementacji
okazało się, że najlepszym sposobem wyznaczenia
tej odległości jest metoda prób i błędów. Co więcej
odległość kamer dla jednej sceny nie musi sprawdzić
4/2011
się, dla inaczej ustawionych aktorów. Pozostałe parametry
związane z ruchem kamer, a co za tym idzie ich
pozycja oraz kierunek, są ściśle określone. Po pierwsze
obie kamery, tak jak nasze oczy, spoglądają z różnych
pozycji na ten sam punkt w przestrzeni. Po drugie,
jeżeli chodzi o ich ruch musimy je potraktować, jak
jeden obiekt – gdy obracamy lub przemieszczamy jedną
kamerę, pozycja drugiej również musi zostać odpowiednio
zmodyfikowana.
Implementacja CPU
Zacznijmy od implementacji, w której obraz dla lewego
i prawego oka zostanie wpierw zapisany do dwóch
tekstur. Skorzystamy do tego z dwóch instancji klas
RenderTarget, który w XNA 4.0, to nowość, dziedziczy
z klasy Texture2D. Następnie tekstury te złożymy piksel
po pikselu w jedną teksturę biorąc składową czerwoną
pierwszego obrazu oraz zieloną i niebieską drugiego.
Tak przygotowany obraz wyświetlimy na ekranie
korzystając z obiektu SpriteBatch (obiekt używany
w aplikacjach z grafiką 2D). Muszę jednak od razu
ostrzec, że składanie dwóch obrazów składających
się np. z 800 x 600 pikseli oznacza pętlę z 480 tysiącami
iteracji. Nawet superszybki procesor CPU może
temu nie podołać w 1/60 sekundy, jaką mamy na przygotowanie
obrazu. I to nawet jeżeli pętlę tą zrównoleglimy
korzystając z nowej w .NET 4.0 metody Parallel.For
(element biblioteki TPL). Mimo to zaczniemy od
Listing 2. Inicjacja pól wymienionych w listingu 1.
protected override void Initialize()
{
}
gd = graphics.GraphicsDevice;
Stereoskopia. Implementacja w XNA 4.0
PresentationParameters pp = gd.PresentationParameters;
takiej „naiwnej” implementacji, aby lepiej zrozumieć
ideę składania anaglifów.
Zacznijmy od zdefiniowania nowych pól klasy Game1,
które odpowiedzialne będą za przechowanie
dwóch osobnych pozycji kamer, kolorów filtrów, czy
wreszcie za przechowanie referencji do obiektów-celów
renderowania, jakie użyjemy w kodzie. Przedsta-
renderTargetLeft = new RenderTarget2D(gd, pp.BackBufferWidth, pp.BackBufferHeight, false,
gd.DisplayMode.Format, DepthFormat.Depth24);
renderTargetRight = new RenderTarget2D(gd, pp.BackBufferWidth, pp.BackBufferHeight, false,
effect = new BasicEffect(gd);
gd.DisplayMode.Format, DepthFormat.Depth24);
effect.VertexColorEnabled = false;
effect.Projection = Matrix.CreatePerspective(2.0f * gd.Viewport.AspectRatio, 2.0f, 1.0f, 100.0f);
effect.EnableDefaultLighting();
cameraPositionLeft = cameraDefaultPositionLeft;
cameraPositionRight = cameraDefaultPositionRight;
viewLeft = Matrix.CreateLookAt(cameraPositionLeft, Vector3.Zero, Vector3.Up);
viewRight = Matrix.CreateLookAt(cameraPositionRight, Vector3.Zero, Vector3.Up);
base.Initialize();
Rysunek 2. Stereoskop Holmesa (źródło Wikipedia)
www.sdjournal.org 53
54
wia je listing 1. Pola te zainicjujemy w metodzie Game1.Initialize
(listing 2). Zwróćmy uwagę na definicję
dwóch celów renderowania i dwóch macierzy widoku.
W metodzie Update powinny znaleźć się instrukcje
obsługujące m.in. klawiaturę, którą możemy sterować
Listing 3. Metoda odpowiedzialna za przygotowanie anaglifu
protected void Draw(GameTime gameTime)
{
}
//Left camera
gd.SetRenderTarget(renderTargetLeft);
gd.Clear(Color.Black);
if (cameraEnabledLeft)
{
}
solid.View = viewLeft;
solid.Draw();
gd.SetRenderTarget(null);
//Right camera
gd.SetRenderTarget(renderTargetRight);
gd.Clear(Color.Black);
if (cameraEnabledRight)
{
}
solid.View = viewRight;
solid.Draw();
gd.SetRenderTarget(null);
//Superimposing
PROGRAMOWANIE GRAFIKI
Color[] imageLeft = new Color[renderTargetLeft.Width * renderTargetLeft.Height];
renderTargetLeft.GetData(imageLeft);
położeniem kamer. W tym ich rozsunięciem. Przykład tej
metody można obejrzeć w kodzie dostępnym na wspomnianej
wcześniej stronie. My natomiast przejdźmy od
razu do kluczowej metody Game1.Draw. Po zmianach
powinna ona przyjąć postać widoczną na listingu 3.
Color[] imageRight = new Color[renderTargetRight.Width * renderTargetRight.Height];
renderTargetRight.GetData(imageRight);
for (int i = 0; i < imageRight.Count(); ++i) imageRight[i] = new Color(imageLeft[i].ToVector3() * filterLeft +
imageRight[i].ToVector3() * filterRight);
Texture2D zlozenie3D = new Texture2D(gd, renderTargetRight.Width, renderTargetRight.Height);
zlozenie3D.SetData(imageRight);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, null, DepthStencilState.Default, null);
spriteBatch.Draw(zlozenie3D, new Rectangle(0, 0, Window.ClientBounds.Width, Window.ClientBounds.Height),
if (showSeparateViews)
{
}
Color.White);
spriteBatch.Draw(renderTargetLeft, new Rectangle(0, 0, Window.ClientBounds.Width / 4, Window.ClientBound
s.Height / 4), Color.White);
spriteBatch.Draw(renderTargetRight, new Rectangle(3 * Window.ClientBounds.Width / 4, 0, Window.ClientBou
spriteBatch.End();
base.Draw(gameTime);
nds.Width / 4, Window.ClientBounds.Height / 4), Color.White);
4/2011
Stereoskopia. Implementacja w XNA 4.0
Rysunek 3. Anaglify - obrazy przygotowane dla okularów dwukolorowych. Od lewej: czerwony-cyjan, czerwony-niebieski i okularów
ColorCode3D (bursztyn-niebieski).
Metoda ta zbudowana jest z trzech bloków. Dwa
pierwsze, odpowiedzialne za renderowanie sceny
z pozycji lewego i prawego oka, są bliźniaczo podobne.
W każdym ustawiany jest cel renderowania,
czyszczony jego bufor, ustawiana macierz widoku
zgodnie z pozycją lewej i prawej kamery i wreszcie
wywoływana jest metoda odpowiedzialna za rysowanie
bryły. W efekcie powstają dwie tekstury.
Następnie przechowywane w nich piksele kopiujemy
do dwóch jednowymiarowych tabel z elementami
typu Color i składamy w jeden obraz z odpowiednio
zmieszanymi składowymi RGB. Owo składanie to
wyróżniona w kodzie pętla, której wykonanie zajmuje
niemal cały używany przez program czas procesora
CPU. Wobec tego z pewnością warto ją zrównoleglić,
choćby używając metody Parallel.For, nowości
w platformie .NET 4.0:
Parallel.For(0, imageRight.Count(), i => {
imageRight[i] = new Color(imageLeft
[i].ToVector3() * filterLeft + image
Right[i].ToVector3() * filterRight);
});
Uzyskany w ten sposób obraz wyświetlamy na całym
obszarze okna korzystając z obiektu SpriteBatch. Dodatkowo
rysujemy w jego górnych rogach obie pierwotne
tekstury z widokami sceny z pozycji lewego
i prawego oka.
W powyższym kodzie nie ma tak naprawdę niczego
zaskakującego lub nowatorskiego. Co gorsze, jego
wykonanie, nawet po zrównolegleniu, zajmuje
więcej czasu, niż czas miedzy kolejnymi wywołaniami
metod Update i Draw. Przez to własność GameTime.IRunningSlowly
jest permanentnie włączona. Mimo
to efekt, jaki zobaczymy na ekranie, oczywiście
pomijając przestoje związane z problemami z wydajnością,
jest naprawdę zadowalający. Zobaczymy na
ekranie obraz analogiczny do tego widocznego na ry-
sunku 3 (lewy). Możemy więc nałożyć okulary i sprawdzić
czy pojawi się wrażenie głębi.
Kolor filtrów użytych do składania obrazu możemy
dobrać modyfikując inicjacje pól filterLeft i filterRight.
W tej chwili jest to czerwień i cyjan (por. listing 1 i rysunek
4). Dla okularów czerwono-niebieskich kolory filtrów
powinny być następujące:
Vector3 filterLeft = new Vector3(1, 0, 0);
Vector3 filterRight = new Vector3(0, 0, 1);
Jak widać z rysunku 3, środkowy filtry czerwony i niebieski
stosowane razem nie zapewniają poprawnego
odwzorowania kolorów. Kolory tych filtrów nie są dopełniające
(ich sumą nie jest biel). W przypadku okularów
bursztynowo-granatowych (ColorCode 3D) filtry
powinny być natomiast następujące:
Vector3 filterLeft = new Vector3(0.85f, 0.85f, 0.3f);
Vector3 filterRight = new Vector3(0.15f, 0.15f, 0.7f);
Z CPU do GPU
Mimo swojej prostoty i niezawodności przedstawiona
powyżej metoda ma jedną zasadniczą i dyskwalifikującą
ją wadę – pętla po tekselach, której zadaniem
jest złożenie obu tekstur, jest zabójcza dla wydajności
całego programu. Nie pomaga nawet wykorzystanie
wszystkich rdzeni CPU. Co więc możemy zrobić?
Przenieśmy te obliczenia na GPU. Tam zamiast kilku,
będziemy mieli kilkaset rdzeni. To jest w końcu procesor
przystosowany do masywnie równoległych obliczeń.
Aby wykorzystać GPU w XNA musimy przygotować
efekt – program napisany w języku HLSL. To język blisko
spokrewniony z C, ale zawierający dodatkowe
wbudowane typy (m.in. wektory dwu-, trój- i czteroelementowe,
macierze o takich rozmiarach i funkcje operujące
na tych typach), a ponadto mechanizm semantyk
określający z jaką własnością werteksu związana
www.sdjournal.org 55
56
PROGRAMOWANIE GRAFIKI
Rysunek 4. Obraz z lewej i prawej kamery po nałożeniu �ltrów czerwonego i cyjanowego.
jest deklarowana zmienna. W efekcie powinny znaleźć
się co najmniej dwie funkcje: shader werteksów
zajmujący się obróbką poszczególnych werteksów wysłanych
z aplikacji oraz shader pikseli wywoływany dla
każdego piksela rysowanej bryły (zob. ramka „Potok
renderowania”).
Kod efektu, który powinniśmy dodać do podprojektu
StereoscopyContent (plik typu Effect file z rozszerzeniem
.fx) widoczny jest na listingu 4. Jego zasadniczą
funkcją jest zastąpienie pętli widocznej na listingu 3.
W tym celu musimy do pamięci karty graficznej przesłać
obie tekstury uzyskane w dwóch pierwszych przebiegach
renderowania. Dlatego efekt zaczyna się od
definicji dwóch parametrów – tekstur oraz związanych
z nimi próbników. Następnie zdefiniowane są dwa filtry
– dla lewego i prawego okularu. Po nich znajduje
się struktura opisująca werteks. W zasadzie potrzebna
jest nam jedynie współrzędna teksturowania tj. wyrażone
w pikselach położenie na teksturze (semantyka
TEXCOORD0), ale musimy również dodać współrzędne
położenia werteksu (semantyka POSITION0) wyrażone
w układzie własnych modelu – tego domagać
będzie się kompilator. Nie będziemy ich jednak używać.
Naszym prawdziwym „inputem” będą bowiem nie
werteksy, a piksele dwóch tekstur.
Przyjrzyjmy się teraz funkcjom VS i PS. To one będą
wykonywane przez Vertex i Pixel Shadery. Funkcja
VS to w zasadzie nic interesującego – przekazuje
jedynie werteksy do interpolatora i pasteryzatora nie
modyfikując ich w żaden sposób. Argumentem funkcji
PS są zinterpolowane współrzędne. Nas interesują tylko
współrzędne teksturowania, które umożliwiają odczytanie
właściwych tekselów z obu tekstór. I to właśnie
robią dwie pierwsze instrukcje funkcji PS (wywołania
funkcji tex2D). Dalsza część kodu odpowiada zawartości
pętli z listingu 3. Miesza teksele z obu tekstur
z wagami odpowiadającym użytym filtrom. W przypadku
okularów czerwono-cyjanowych, mieszanie to pole-
Potok renderowania
Shadery werteksów i shadery pikseli to programowalne jednostki kart graficznych. Tymi samymi nazwami określane są również
wykonywane przez nie funkcje (u nas są to odpowiednio VS i PS). Danymi wejściowymi shadera werteksów są własności werteksu
przesłane z aplikacji lub odczytane z bufora werteksów. W typowych sytuacjach, ale nie w naszym przykładzie, zadaniem
funkcji shadera werteksów jest przeliczenie położenia werteksu ze współrzędnych modelu, poprzez współrzędne sceny do układu
odniesienia kamery. Oznacza to ich przemnożenie kolejno przez macierz świata, widoku i rzutowania. Przeliczone położenie
trafia do rasteryzatora, który odpowiada za przydzielenie pikseli do trójkątów. To dzięki rasteryzatorowi do shadera pikseli trafiają
nie tylko trzy piksele odpowiadające wierzchołkom każdego trójkąta, ale także wszystkie piksele z jego wnętrza. Pozostałe
własności werteksu, a więc w naszym przypadku tylko współrzędne teksturowania, trafiają do interpolatora. Dokonuje on interpolacji
tych wielkości dla wszystkich pikseli wewnątrz trójkątów. Tak interpolowane dane o werteksie stanowią dane wejściowe
shadera pikseli, który jest odpowiedzialny przede wszystkim za ustalenie ostatecznego koloru każdego piksela. W kartach graficznych
zgodnych z DirectX 10 podział na jednostki uruchamiające shadery pikseli i werteksów stał się umowny. Jednostki są teraz
uniwersalne i mogą być przydzielane dynamicznie do jednego, bądź do drugiego zadania. Ponadto w DirectX 11 pojawił się
jeszcze shader geometrii, który pozwala na dodawanie i usuwanie werteksów. Jednak nie jest on jeszcze niestety obsługiwany
w XNA. Cały proces renderowania został schematycznie przedstawiony na rysunku 5. Pamiętajmy jednak, że niekoniecznie musi
on skończyć wyświetleniem obrazu na ekranie. W naszym programie – zamiast wyświetlać na ekranie, obraz będzie on także zapisywany
do tekstur – zastępczych celów renderowania.
4/2011
ga na użyciu składowej czerwonej pierwszej tekstury
i składowych zielonej i niebieskiej drugiej. Wartością
funkcji PS jest kolor, który użyty zostanie do rysowania
w miejscu określonym przez współrzędne 3D, których
wprawdzie jawnie nie używamy, ale które odpowiadają
położeniu tekseli w oryginalnych teksturach (obie tekstury
mają rozmiar ekranu). Oznacza to, że efektu powinniśmy
użyć do rysowania na kwadracie dokładnie
wypełniającym ekran. Tak też zrobimy.
Aby użyć powyższego efektu w programie zdefiniujmy
pole typu Effect:
Effect superimposingEffect;
Następnie w metodzie Game1.LoadContent wczytujemy
nowy efekt poleceniem:
superimposingEffect = Content.Load("Stereoscopy
Effect");
Pozostaje jedynie przygotować kwadrat przesłaniający
ekran. Jego werteksy są następujące:
VertexPositionTexture[] werteksyEkranu = new
{
VertexPositionTexture[4]
new VertexPositionTexture(new Vector3(-1, 1, 0f), new
����������������������
�����������������
Vector2(0, 0)),
�������������
���������
���������
����������������
��������������
�����������������
�����������
�������
Stereoskopia. Implementacja w XNA 4.0
�����������������
��������������������
������������ ������������ ������������
����������������
Rysunek 5. Uproszczony schemat potoku renderowania. Nie ma
w nim shadera geometrii, do którego i tak jednak nie ma jeszcze
dostępu w XNA 4.0
Listing 4. Zapisany w pliku StereoscopyEffect.fx kod efektu
odpowiedzialny za złożenie i wyświetlenie tekstur
texture TextureLeft;
sampler TextureSamplerLeft = sampler_state
{
texture = ;
magfilter = POINT;
minfilter = POINT;
mipfilter = POINT;
AddressU = Clamp;
AddressV = Clamp;
};
texture TextureRight;
sampler TextureSamplerRight = sampler_state
{
texture = ;
magfilter = POINT;
minfilter = POINT;
mipfilter = POINT;
AddressU = Clamp;
AddressV = Clamp;
};
float3 FilterLeft = float3(1,0,0);
float3 FilterRight = float3(0,1,1);
struct Vertex
{
float4 Position : POSITION; //it's not used
float2 TexCoord : TEXCOORD0;
};
Vertex VS(Vertex input)
{
Vertex output;
output.Position = input.Position;
output.TexCoord = input.TexCoord;
return output;
}
float4 PS(Vertex input): COLOR0
{
float4 texelLeft = tex2D(TextureSamplerLeft,
input.TexCoord);
float4 texelRight = tex2D(TextureSamplerRight,
input.TexCoord);
float4 color;
color.rgb = FilterLeft*texelLeft +
FilterRight*texelRight;
color.a = 1;
return color;
www.sdjournal.org 57
}
technique Technique1
{
pass Pass0
{
VertexShader = compile vs_2_0 VS();
PixelShader = compile ps_2_0 PS();
}
}
58
Listing 5. Ostateczna postać metody Draw
protected void Draw(GameTime gameTime)
{
}
//Left camera
gd.SetRenderTarget(renderTargetLeft);
gd.Clear(Color.Black);
if (cameraEnabledLeft)
{
}
solid.View = viewLeft;
solid.Draw();
gd.SetRenderTarget(null);
//Right camera
gd.SetRenderTarget(renderTargetRight);
gd.Clear(Color.Black);
if (cameraEnabledRight)
{
}
solid.View = viewRight;
solid.Draw();
gd.SetRenderTarget(null);
//Superimposing
PROGRAMOWANIE GRAFIKI
superimposingEffect.CurrentTechnique = superimposingEffect.Techniques[0];
superimposingEffect.Parameters["TextureLeft"].SetValue(renderTargetLeft);
superimposingEffect.Parameters["TextureRight"].SetValue(renderTargetRight);
superimposingEffect.Parameters["FilterLeft"].SetValue(filterLeft);
superimposingEffect.Parameters["FilterRight"].SetValue(filterRight);
foreach (EffectPass pass in superimposingEffect.CurrentTechnique.Passes)
{
}
pass.Apply();
gd.DrawUserPrimitives(PrimitiveType.TriangleStrip, werteksyEkranu, 0, 2);
if (showSeparateViews)
{
}
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, null, DepthStencilState.Default, null);
spriteBatch.Draw(renderTargetLeft, new Rectangle(0, 0, Window.ClientBounds.Width / 4, Window.ClientBound
s.Height / 4), Color.White);
spriteBatch.Draw(renderTargetRight, new Rectangle(3 * Window.ClientBounds.Width / 4, 0, Window.ClientBou
spriteBatch.End();
base.Draw(gameTime);
nds.Width / 4, Window.ClientBounds.Height / 4), Color.White);
4/2011
};
new VertexPositionTexture(new Vector3(1, 1, 0f), new
Vector2(1, 0)),
new VertexPositionTexture(new Vector3(-1, -1, 0f), new
Vector2(0, 1)),
new VertexPositionTexture(new Vector3(1, -1, 0f), new
Vector2(1, 1))
Po tych przygotowaniach możemy użyć nowego efektu
do połączenia tekstur i ich wyświetlenia. W tym
celu należy zmodyfikować końcówkę metody Game1.Draw
(listing 5). Po tej zmianie nie będzie już
z pewnością problemu z nienadążaniem za częstością
odświeżania. Program zaczyna działać płynnie,
a jednocześnie użycie CPU spada niemal do zera.
Nasza implementacja animowanego anaglifu jest
trójprzebiegowa. Dwa razy wywoływany jest standardowy
BasicEffect i na koniec napisany samodzielnie
StereoscopyEffect. Nie wątpię, że korzystając z różnych
trików można by ilość przebiegów ograniczyć
do dwóch. Szczególnie jeżeli skorzystamy z mieszania
kolorów (alpha blendingu), który jednak narzuca
pewne ograniczenia w sposobie rysowania sceny. Nasza
implementacja jest wolna od tych założeń, a jednocześnie
na tyle ogólna, że może być bez trudu użyta
w dowolnym programie XNA. Wszystko dzięki temu,
że dwa pierwsze przebiegi mogą być zrealizowane
w zupełnie dowolny sposób.
Stereoskopia. Implementacja w XNA 4.0
Więcej w sieci
WOJCIECH STAŃCZYK, JACEK MATULEWSKI
Wojciech Stańczyk - Programista pracujący na co dzień w �rmie DOT.NET (http://www.dot.net.pl), gdzie zajmuje się tworzeniem
rozwiązań programistycznych dla platformy Microsoft .NET. Poza pracą stale rozwija swoje umiejętności w zakresie programowania
gra�ki komputerowej, w tym efektów stereoskopowych. Od ponad 12 lat wielki pasjonata chińskich sztuki walki Kung Fu
Tang Lang Men.
Jacek Matulewski - Fizyk zajmujący się na co dzień optyką kwantową i układami nieuporządkowanymi na Wydziale Fizyki,
Astronomii i Informatyki Stosowanej Uniwersytetu Mikołaja Kopernika w Toruniu. Jego specjalnością są symulacje ewolucji
układów kwantowych oddziaływujących z silnym światłem lasera.
Od 1998 interesuje się programowaniem dla systemu Windows. Ostatnio zainteresowany platformą .NET i językiem C#. Wierny
użytkownik kupionego w połowie lat osiemdziesiątych "komputera osobistego" ZX Spectrum 48k.
Artykuł jest fragmentem pracy magisterskiej WS napisanej pod kierunkiem JM.
• http://theinstructionlimit.com/?p=123 – Strona gry Hyper-
CUBE, która była naszą inspiracją, a blog jest twórców –
przewodnikiem.
www.sdjournal.org 59
60
TESTOWANIE OPROGRAMOWANIA
Czy zauważyłeś goryla?
Praktyczne umiejętności dostrzegania błędów aplikacji
Jakość jest ważna, zgoda? Nie tylko jako dobro luksusowe,
ale przede wszystkim jako sposób na podniesienie
wydajności i na to, aby budowanie aplikacji było łatwiejsze,
skuteczniejsze i przyjemniejsze. Wiele jest sposobów
zapewnienia jakości, niektóre bardzo wyrafinowane, ale nic
nie zastąpi podstawowej umiejętności SPRAWDZANIA, czy to,
co zrobiono, zrobiono dobrze. Sprawdzać muszą programiści, projektanci,
analitycy i użytkownicy, no i oczywiście zawodowi sprawdzacze - testerzy.
Na każdym poziomie, podczas każdego kroku wytwarzania.
Dowiesz się:
• Dlaczego tak łatwo nie zauważyć czegoś ważnego?
• Testowanie staromodne i testowanie agile
• Jak mieć dokumentację, ale jej nie robić?
• Jak zrobić ekologiczny projekt - dbałością o środowiska!
• Kim są eksploracyjni pogromcy błędów i goryli?
Kto nie zauważył goryla?
Proszę uruchomić sobie film www.dogonews.com/
2010/7/19/video-of-the-week-can-you-spot-the-gorilla
i odpowiedzieć na pytanie: ile razy podają do siebie
piłkę zawodniczki w białych strojach?
Nie wiem, jak czytelnik, ale większość obserwatorów
tego filmu – tak jest, większość! – w ogóle nie zauważa
goryla, tak jest skoncentrowana na niełatwym zadaniu
liczenia podań. Jeśli jednak instrukcja eksperymentatora
każe skoncentrować się nie na liczeniu podań, lecz
na poszukiwaniu czegoś niezwykłego, wynik jest zupełnie
inny – zdecydowana większość zauważa goryla.
To samo odnosi się do ważnego i modnego w ostatnim
czasie tematu jakości oraz testowania aplikacji,
niezależnie od tego, czy testerem jest:
• doświadczony programista, szukający błędów
– cytuję: „w sytuacji, gdzie nawet Power Mockito
nie jest w stanie zmokować klas, bowiem są
one w taki sposób finalizowane, że mokowanie poprzez
oszukanie classloadera nie działa”…
• … czy też użytkownik końcowy, na przykład aktuariusz
zajmujący się „zagadnieniami modelowania
rozkładu łącznej wartości szkód w modelu ryzyka
łącznego lub indywidualnego”.
Powinieneś wiedzieć:
• Jak się programuje?
• Ile kosztują ukryte błędy?
• Z czym są w projektach wieczne kłopoty?
• Dlaczego ludzie się sprzeczają i nie lubią robić dokumentacji?
• Czego zwykle brakuje w środowiku testowym?
Obaj, lub obie, mogą goryla przeoczyć, dopuścić do
użytku błędną aplikację. Co zrobić, aby to ryzyko
ograniczyć?
Najważniejsze jak się kończy,
a nie jak się zaczyna!
Wiele jest ciekawych pozycji na temat projektowania
testów – jak to zrobić, aby paroma tysiącami dobrze
dobranych testów miarodajnie sprawdzić program,
który będzie używany przez tysiące osób na setki milionów
różnych sposobów? Drugi popularny temat to
zarządzanie procesem zapewnienia jakości.
4/2011
Ten artykuł zajmuje się sprawą podstawową, choć
zaniedbywaną i w teorii, i w praktyce: jak dobrze wykonywać
testy? Tak, aby zauważyć błędy, aby nie
przegapić goryla! Nawet najbardziej wyszukane metody
projektowania testów i najgenialniejsze zarządzanie
same błędów nie wychwycą – zrobi to dopiero wykonywanie
testów.
Wykonywanie testów
a sposób tworzenia aplikacji
Choć specjaliści od testowania z upodobaniem i nie
bez racji podkreślają, że sztuka testowania to niezależna,
samodzielna dziedzina, a nie ubogi krewny programowania
czy zarządzania projektami, to jednak
skuteczny sposób testowania zależy w dużym stopniu
od tego, jak się tworzy oprogramowanie.
Na przykład pod pewnymi względami testowanie
jest łatwiejsze w tradycyjnych, gęsto udokumentowanych
projektach realizowanych metodą kaskadową,
bowiem jest tam czas na względnie staranne przygotowanie
specyfikacji i procedur testowych, dzięki czemu
mniejszy ciężar spoczywa na samym ich wykonywaniu.
Z drugiej strony, testowanie bywa łatwiejsze w metodach
zwinnych (agile, np. popularny dziś SCRUM),
bowiem:
• Czas, jaki upływa między pisaniem kodu a jego
testowaniem, jest znacznie krótszy, dzięki czemu
wykonując testy, wciąż jeszcze pamięta się, co
i dlaczego zostało zaprogramowane.
• Testując wielokrotnie i już od początku projektu,
jest się mniej narażonym na chroniczne w tradycyjnych
projektach zjawisko eksplozji wykrywania
błędów na samym końcu, tuż przed wdrożeniem.
• Przy podejściu zwinnym słabszy zwykle bywa tak
zwany „efekt walca” 1 , polegający na zwalaniu całej
odpowiedzialności za szukanie błędów na działający
- pięć minut przed dwunastą - zespół testowy.
Ryzyko i priorytety
Aby uniknąć sytuacji, kiedy dużego goryla dostrzegamy
w ostatniej chwili, trzeba znać, co dla odbiorcy, dla
użytkownika aplikacji jest najważniejsze, i to testować
przede wszystkim, już na początku. Jeśli tego nie wiadomo,
testowanie jest chaotyczne, nerwowe, a w takiej
Czy zauważyłeś goryla?
sytuacji niejeden goryl zostanie przeoczony – ze szkodą
dla wszystkich. Aby tego uniknąć, potrzeba przede
wszystkim znakomitej współpracy między programistami,
projektantami, analitykami systemu i klientem, a jak
z tym zwykle jest – dobrze wiemy 2 . Tutaj znów metody
agile mogą się wydawać – zwłaszcza programistom
– cudownym rozwiązaniem, ale powiedzmy sobie: agile
nie jest lekarstwem na wszystko. Zleceniodawcy biznesowi
muszą się nauczyć precyzyjnie formułować
swoje wymagania, a z drugiej strony, programiści muszą
się nauczyć lepiej słuchać zleceniodawców. Razem,
najłatwiej dostrzec goryla!
Tyle dokumentacji, że brak czasu na pracę
Poważną przeszkodą w dostrzeganiu goryli jest konieczność
dokumentowania testów: pisanie raportów,
tworzenie logów, zgłoszeń błędów… Przecież ten, kto
jest pochylony nad formularzem raportowania goryli,
ten ich nie obserwuje! Miłośnicy dokumentacji podkreślają,
że bez odpowiedniej papierologii w projektach
szerzy się chaos i marnotrawstwo – i wiele w tym racji.
Z drugiej strony, przeciwnicy dokumentacji podnoszą
fakt, że – jak głosi manifest agile 3 – „cenimy działające
oprogramowanie bardziej niż obszerną dokumentację”,
a papierologia jest czasem wyrazem organizacyjnej
sklerozy i nerwicy, a nie rzeczową potrzebą.
Jak pogodzić te sprzeczne racje, jak sprawić, aby
wilk był syty, a owca cała? Jedna jest na to rada: przywrócić
niewolnictwo. Wtedy Panowie 4 będą testować,
a niewolnicy – notować i raportować. Skoro jednak
niełatwo przyjdzie przepchnąć stosowną ustawę
przez Sejm, tedy pozostaje drugie wyjście – automatyzacja.
Pewna znana firma informatyczna, której główna
siedziba mieści się, jak należy, w Kalifornii, ma jeden
z działów tworzenia swego zaawansowanego oprogramowania
w miejscu mniej popularnym: w Nowosybirsku
5 . Kiedyś, gdy gościliśmy właściciela tej firmy
w Warszawie na comiesięcznym spotkaniu SPIN 6 ,
uruchomił on swoją przenośną „dyrektorską deskę
rozdzielczą” 7 i powiedział – było to w poniedziałek wieczorem:
„oho, w Nowosybirsku znów balowali w sobotę
i w niedzielę!”. Krzywe błędów z automatycznie rejestrowanych
wyników automatycznych testów statycznych
oraz jednostkowych szły ostro w górę dla po-
1 Od słów piosenki Młynarskiego „Przyjdzie walec i wyrówna” – chałupnicze tłumaczenie znanego określenia Hansa Schaefer’a „testing is not
a vacuum-cleaner”, użytego w artykule „What a Tester Must Remember Even After Midnight”.
2 Zwykle jest kiepsko.
3
Manifesto for Agile Software Development: http://agilemanifesto.org/
4
Panie także, oczywiście!
5
Osoby, które zgadną, o jaką firmę chodzi, proszone są o przysłanie maila na adres autora: bogdan@victo.eu w celu otrzymania nagrody.
6 SPIN Warszawa: www.spin-warszawa.pl.
7 CEO dashboard
www.sdjournal.org 61
62
niedziałkowej produkcji… I na tym koniec: kierownictwo
nie musiało marudzić, a programiści – narzekać,
nikt nie marnował czasu na ręczne piszanie sążnistych
raportów. Automatyczne testy spowodowały, że
błędów po prostu nie dało się przeoczyć, a więc i musiało
się je naprawić. Ten przykład nie wymaga chyba
komentarza.
Tylko uwaga – powyższy akapit jest wprawdzie bezkrytyczną
i entuzjastyczną pochwałą automatycznego
raportowania, ale niekoniecznie – automatycznego testowania!
Na pewno względnie nietrudno jest napisać
program, analizujący GUI pokazanego na początku filmu
tak, aby automatycznie rejestrować liczbę podań
piłki, ale napisanie programu, który dorównałby człowiekowi
w dostrzeganiu wchodzących na boisko nieoczekiwanych
goryli, to niemożliwe przedsięwzięcie.
Bo przecież zamiast goryla na boisko może wejść żółw
albo żyrafa, a może wjechać tramwaj? – nie da się tego
rodzaju logiki zaimplementować w kodzie. Nic w roli
wykrywacza goryli nie zastąpi człowieka – więc tym
bardziej trzeba te unikalne ludzkie możliwości chronić,
przy pomocy automatyzacji, przed marnowaniem ich
do rutynowej, łatwej do zautomatyzowania pracy – raportowania
i dokumentacji.
Testowanie a debugowanie
Dwaj bracia?
Kiedyś, w średniowieczu
informatyki – w latach
50-ych i 60-ych minionego
wieku – testowanie
i debugowanie
było niczym bracia bliźniacy:
wykonywano je
naprzemiennie i jeden
proces płynnie przechodził
w drugi.
Dziś są jako Ezaw i Jakub: nadal bliźniacy, ale zaplątani
w chroniczny konflikt.
Wracając do naszego similis 8 przykładu z gorylem:
dostrzeganie goryla to testowanie, a jego złapanie
i wyprowadzenie poza boisko – to już debugowanie.
Jeśli jedno i drugie ma robić ta sama osoba, to jasne,
że łapiąc jednego goryla, mało mamy czasu na uważne
obserwowanie, czy na boisku nie ma innych goryli,
zaś obserwując – nie łapiemy, czyli jedna czynność
zabiera czas drugiej. Ponadto, umiejętności niezbędne
do łapania i usuwania goryli są czasem zupełnie inne,
niż te pomocne do skutecznej obserwacji, co bywa
jedną z przyczyn tradycyjnych konfliktów między programistami
a testerami.
Żeby szukanie błędów w aplikacjach nie było tylko
sztuką dla sztuki, ale dawało możliwość ich po-
8 Similis (łac.) - małpi
TESTOWANIE OPROGRAMOWANIA
prawienia, konieczne jest sprawne debugowanie i dobra
współpraca tych, co sprawdzają, z tymi, co naprawiają.
Jak z tym bywa w rzeczywistości, dobrze wiemy:
każdy programista z lubością zacytuje liczne przykłady
zgłoszeń błędów, które były głupie albo niejasne,
zaś każdy tester albo użytkownik oprogramowania
odpowie równie licznymi przykładami, kiedy programiści
zasłaniali się tym, że nie są w stanie odtworzyć
awarii, albo poświęcali dużo energii, by wykazać,
że oczywisty błąd jest… poprawnym działaniem. Takie
gry i zabawy nazywa się niekiedy „ping-pongiem
zgłoszeń błędów”. Zabierają one czas wszystkim, psują
atmosferę, a na pewno nie służą skuteczności ani
sprawności.
Cytowane powyżej „nie da się odtworzyć błędu
- u nas działa” nie musi świadczyć o złej woli programistów,
lecz o tym, że pewne błędy programu mogą
się pojawiać w formie awarii w pewnych tylko środowiskach
lub konfiguracjach, a w innych – pozostawać
w utajeniu.
Teoria „testologii” niewiele mówi na ten temat, ale
w praktyce to własnie kwestie środowiska, konfiguracji
oraz danych testowych są przyczyną większości awarii,
a sprawy z nimi związane pochłaniają nawet ponad
połowę czasu i zasobów, wykorzystywanych do zapewnienia
jakości.
Środowisko
Zwykle w projektach IT mamy do czynienia z co najmniej
trzema typami środowisk: środowiskiem wytwarzania,
gdzie pracują programiści, środowiskiem testowym,
gdzie wykonuje się większość testów, oraz środowiskiem
docelowym. Różnice między nimi są przyczyną
wielu kosztownych i czasochłonnych kłopotów.
Jeśli środowisko testowe nie jest identyczne z programistycznym,
wówczas testerzy często zgłaszają
awarie, których programiści nie są w stanie odtworzyć
– aby to zrobić, muszą swoje debugowanie przenieść
na środowisko testowe. Czy to stwarza szereg
trudności? – oczywiście, że tak. Byłem kiedyś testerem
w projekcie, gdzie zastawszy rano na w laboratorium
testowym kilku programistów, zajętych debugowaniem,
wiedziało się, że tego dnia niewiele uda się
przetestować. Zacząłem więc przychodzić do pracy
wcześniej – wtedy cierpieli programiści, którzy nie mogli
debugować. Około godziny piątej rano kierownictwo
projektu dostrzegło problem i znalazło rozwiązanie
– poniewczasie, bo nie uwzględniono go przy planowaniu
projektu.
Jeśli z kolei środowisko, konfiguracje i dane testowe
nie są identyczne z docelowymi, mamy inne kłopoty,
często jeszcze bardziej dotkliwe. Mało jest na świecie
rzeczy gorszych, niż wypuścić starannie przete-
4/2011
stowane oprogramowanie po to, by przekonać się, że
u klienta nie działa.
Dla dużych, złożonych czy wręcz rozproszonych
aplikacji budowa, konfiguracja i dostęp do środowiska
testowego, dostęp do danych produkcyjnych (bywają
poufne), odpowiednie uprawnienia dla testerów-użytkowników,
odpowiedzialność za środowisko – niejednemu
sen z oczu spędzają.
Nie w każdym środowisku występują te same goryle!
Biologowie wyróżniają goryle górskie, nizinne, zachodnie…
Nieustraszeni, eksploracyjni
pogromcy goryli
Jak być spostrzegawczym i zauważać goryle? Jako
informatyk oraz, niestety! – psycholog, co mi nie
raz zarzucano, miałbym teraz wielką ochotę zaserwować
Czytelnikom serię przerażających tematów, takich
jak tak zwana uważność (mindfulness), buddyzm,
psychologia pozytywna, teorie motywacji, psychologia
poznawcza, teorie nacisku grupowego, kreatywność,
typy osobowości… ale proszę zachować spokój.
Wprawdzie nadal jestem zdania, że te psychologiczne
czynniki mają dla umiejętności spostrzegania błędów
znaczenie co najmniej równie duże, co znajomość stosownych
technologii lub zasad biznesowych, ale na
szczęście istnieje metoda testowania aplikacji, która
w sporym stopniu wykorzystuje zdobycze psychologii:
testowanie eksploracyjne.
Historia eksploracji
W roku 1999 pojawiła się kultowa dziś pozycja trzech
autorów: Cema Kanera, Hunga Q. Nguena oraz Jacka
Falka Testing Computer Software.
Książka Cem Kaner Hung Q. Nguen
Jej autorzy wyłożyli zasady podejścia do testowania,
które nazwali „testowaniem eksploracyjnym”, a które
najkrócej definiuje się jako jednoczesne uczenie się
działania aplikacji, wykonywanie testów i projektowanie
testów.
Czy zauważyłeś goryla?
Zalety eksploracji
Główne zalety podejścia eksploracyjnego to:
• znacznie krótsze przygotowania (nie tworzy się
specyfikacji testów)
• szybkie znajdowanie najważniejszych błędów (testowanie
polega na swego rodzaju inteligentnej
zabawie testowaną aplikacją lub jej elementami)
• więcej radości oraz intelektualnej stymulacji, niż
przy wykonywaniu testów zaprojektowanych zawczasu.
James Bach, inny znany guru testowania
eksploracyjnego 9 , pisze: „Testowanie
eksploracyjne to silna i dająca
wiele radości metoda testowania.
W pewnych okolicznościach bywa
o rząd wielkości skuteczniejsza
niż testowanie tradycyjne, przy użyciu
specyfikacji.
Nie ma chyba testera, który nigdy nie zastosował
podejścia eksploracyjnego – choćby intuicyjnie. Mimo
to, niewiele jest badań w tej dziedzinie, i nie cieszy się
ona zbyt wielkim poważaniem.
Czas najwyższy, aby skończyć z oporem wobec tej
metody, i aby publicznie uznać ją za to, czym jest: naukowym
myśleniem w czasie rzeczywistym. Przyjaciele
– testowanie eksploracyjne jest dobre!” 10
Jak to zrobić w praktyce?
Testowania eksploracyjnego trudno nauczyć się
z podręczników, ono wymaga przede wszystkim treningu.
Osoby zainteresowane bez trudu znajdą liczne
angielskojęzyczne źródła w Internecie. Po trening
praktyczny zwykle trzeba wybierać się do Stanów, ale
już wkrótce będzie okazja zrobić to w Polsce, miejmy
nadzieję, że nie ostatnia. W kwietniu w Warszawie
i we Wrocławiu odbędą się dwudniowe praktyczne treningi
testowania eksploracyjnego, prowadzone przez
trenera z firmy, należącej do jednego z trzech ojcówzałożycieli
podejścia eksploracyjnego, Hunga Q. Nguena.
Do zobaczenia na treningu!
Nawet jeśli nie masz czasu na ten trening, SPIN Warszawa
zaprasza 4 kwietnia o godzinie 19:00 na spotkanie
z wice-prezesem firmy LogiGear, Michaelem
Hackettem, na temat testowania eksploracyjnego
(www.spin-warszawa.pl).
BOGDAN BEREZA-JAROCINSKI
Kontakt z autorem: bogdan@victo.eu
9 Specjalność Jamesa Bacha to tak zwane „zarządzanie testowaniem w sesjach” (session-based test management) – swoista metoda organi-
zacji testów eksploracyjnych.
10 http://www.satisfice.com/articles/what_is_et.shtml
www.sdjournal.org 63
64
TESTOWANIE OPROGRAMOWANIA
Kariera w testach
oprogramowania
Artykuł kierowany jest do osób, które są na etapie wyboru
odpowiedniej dla siebie ścieżki rozwoju w branży IT
lub zastanawiają się nad zmianą dotychczasowej drogi
zawodowej. Chciałbym im przybliżyć obszar testów, główne
zadania testera, miejsce zespołu testów w organizacji. Pokażę,
co ciekawego w tym zespole można znaleźć dla siebie.
Dowiesz się:
• Gdzie zacząć karierę w testach
• Jakie są role i specjalizacje w zespole testowym
• Jak może przebiegać kariera w testach
Testowanie – parę słów o …
Testowanie systemów informatycznych jest końcowym
etapem procesu dostarczania oprogramowania. Generalnie
można powiedzieć, że testy realizują potrzebę
zapewnienia jakości tworzonego produktu. Stanowią
punkt kontrolny, w którym następuje ocena przydatności
do użycia. Jeśli chce się stworzyć rozwiązanie informatyczne,
które będzie realizowało wymagania klienta
i na określonym poziomie wspierało jego biznes, to
niezbędne jest testowanie. Wiele firm przekonało się,
że warto zainwestować w testowanie zamiast ponosić
koszt odbudowy wizerunku czy zaufania klientów.
Miejsce zespołu testów
Zespół testów może występować w kilku miejscach,
jest to zależne od specyfiki organizacji. W zasadzie
obowiązkowo taki dział powstaje u producenta
oprogramowania. Niezależnie czy tworzy się produkt
„z pudełka” czy szyje się go na miarę - pod wymagania
klienta, pierwszym miejscem, w którym potwierdza
się zgodność cech produktu z oczekiwaniami jest wewnętrzna
komórka testów. Czasem usługi takie będą
oferowane również klientom. Komórki testów powstają
też w firmach, które pełnią rolę integratora w projektach
informatycznych.
Kolejnym miejscem, w którym tworzy się dział testów
jest organizacja, która występuje na rynku
oprogramowania w charakterze klienta. Mam tu na myśli
zwłaszcza firmy czy instytucje, które posiadają duże
środowiska systemów informatycznych, gdzie funkcjo-
Powinieneś wiedzieć:
• Jak realizowany jest projekt informatyczny
nują złożone procesy zarządzania IT. Zależnie od specyfiki
firmy komórki testów mogą być umiejscowione
w strukturach biznesowych, bądź po stronie technicznej
organizacji.
W pierwszym przypadku taki zespół najczęściej będzie
odpowiedzialny za testy akceptacyjne. Nacisk będzie
położony na weryfikację poprawności rozwiązania
z punktu widzenia operacji biznesowych. Celem testów
będzie walidacja przydatności oprogramowania
w oparciu o wymagania biznesowe. Testerzy będą potrzebować
znajomości procedur i procesów biznesowych
oraz wiedzy o oczekiwaniach klientów – względem
firmy, świadczonych przez nią usług, czy sprzedawanych
produktów. Od takiego zespołu nie wymaga
się szerokiej wiedzy technicznej. Oczywiście, będzie
ona przydatna, ale nie jest niezbędna. Ich rolą
jest ocenić stopień przydatności oprogramowania do
wsparcia funkcji biznesowych. Zadania techniczne będą
w takim modelu przeniesione na specjalistów z innych
komórek.
W innej sytuacji, możemy mieć do czynienia z zespołem
testów utworzonym po stronie IT. Jego zadaniem
będzie wykonanie testów technicznych np. systemowych,
integracyjnych czy wydajnościowych. Zespół
będzie bardziej zorientowany na sprawdzenie czy produkt
spełnia założenia opisane w specyfikacji technicznej,
czy realizuje poprawnie procesy systemowe, czy
komponenty składowe rozwiązania mają zgodne z założeniami
parametry. W takim zespole testerzy będą potrafili
używać SQL, przeanalizować komunikat EAI, ko-
4/2011
zystać z bardziej zaawansowanych narzędzi wsparcia
testów wymagających umiejętności programowania.
W dużych organizacjach mogą występować dwa zespoły
testów – techniczny i biznesowy. Czasem efektywne
będzie łączenie testerów obu profili w jeden zespół.
Zdarza się, że firmy zmieniają swój model działania
i koncentrują się na podstawowym profilu działalności.
Zlecają wtedy na zewnątrz elementy zarządzania IT, jak
rozwój, utrzymanie systemów czy zarządzanie projektami
informatycznymi. W ten model pracy wpisują się także
firmy specjalizujące się w testach oprogramowania,
takie jak Soflab Technology. Daje to możliwość uczestniczenia
w projektach u wielu klientów. Umożliwia poznanie
różnych sposobów prowadzenia projektów, daje
szerszy przegląd standardów i technologii. W takich
firmach duży nacisk kładzie się na dzielenie się wiedzą
i doświadczeniami, a rozwój pracownika jest ściśle
związany z obszarem testowania.
Struktura i role w zespole testów
W przypadku dużych wdrożeń w ramach zespołu testów
odpowiedzialność za poszczególne zadania będzie
podzielona. Najważniejszą osobą, której zadaniem
w projekcie jest koordynacja działań testowych
na wszystkich poziomach jest Kierownik Testów. Jego
najważniejszym obowiązkiem, w pierwszej fazie procesu
testów, jest stworzenie podejścia do testów, w któ-
Kariera w testach oprogramowania
rym przedstawi zakres testów, strukturę podziału prac,
założenia dotyczące środowisk, procedur testowych
oraz metodykę realizacji. Do Kierownika Testów należy
odbiór produktów z etapów poprzedzających, wskazanie
ewentualnego ryzyka Kierownikowi Projektu, a także
zapewnienie niezbędnych zasobów do realizacji testów
i koordynacja prac zespołu. Kierownik Testów buduje
i uzgadnia harmonogram testów. W tej roli najlepiej
sprawdzą się osoby mające wcześniej doświadczenie
w samodzielnym wykonywaniu testów, posiadające
zdolności organizacyjne i dobry kontakt z zespołem.
Kolejną ważną osobą, która zbuduje plan testów i wybierze
odpowiednie techniki do jego realizacji jest Architekt
(lub Projektant) Testów. Osoba na tym stanowisku
odpowiada za identyfikację podlegających weryfikacji
warunków testowych, wybór optymalnych technik dla
sprawdzenia funkcjonalności oraz wskazanie niezbędnych
narzędzi testowych. Architekt testów ma zapewnić
kompletność pokrycia wymagań, przedstawić potrzeby
związane z środowiskiem i danymi testowymi, a także
oszacować pracochłonność zadań testowych. W tym
miejscu potrzebne jest doświadczenie w testach, ale
przede wszystkim dobra znajomość architektury i procesów
systemowych. Posiadając taką wiedzę Architekt
Testów może przedstawić nie tylko przepis na sprawdzenie
nowych funkcjonalności, ale także ocenić wpływ
������������������������������������
�������������������������
�������������
� �������������
�����������������
� ������������������������������
������������
� ��������������������
� ����������������������
����������������
� �������
� ���������
������
����������
������
����������
�����
���������
���������������
�����������
�������������
�����������������
�����������
�����
��������������������
����������������
�����������������������
��������������������������
����������������
�������
����������� ����������
�����������
�����
�������������
������������������
���������
��������
��
����������
�����
�������������
�������������
66
zmian na bieżące procesy i zarekomendować właściwy
zakres testów regresji.
Wreszcie Tester, a więc osoba odpowiedzialna
przede wszystkim za wykonanie scenariuszy testowych
oraz ewidencję problemów. Na nim spoczywa odpowiedzialność
za poprawność merytoryczną weryfikacji. Dobry
tester, oprócz znajomości funkcjonalności i procedury
testowej, ma pomysły i intuicję. Dzięki temu stawia
właściwe pytania, które szybko ujawniają słabości
rozwiązania.
Specjalizacje w testach
W ramach testów oprogramowania istnieje podział
związany ze specyfiką testów. Najczęstsze specjalizacje,
jakie możemy spotkać, to:
• Tester funkcjonalny, który najogólniej bada funkcjonalności
realizowane przez system. Jest to dość
pojemna kategoria, ponieważ można specjalizować
się w systemach ERP, CRM, PRM, systemach rozliczeniowych,
czy OSS. Można koncentrować się
na testach integracyjnych, co wymaga znajomości
technologii integracji aplikacji (EAI) lub na testach
biznesowych.
• Inżynier automatyzacji testów, którego zadaniem
jest implementacja skryptów testowych w jednym
z dostępnych narzędzi wspierających testy automatyczne.
Często osoby zajmujące się tym obszarem
wykonują też testy wydajnościowe.
• Bardzo wąską specjalizacją jest testowanie bezpieczeństwa.
Testy penetracyjne mogą obejmować
sprzęt, urządzenia sieciowe i aplikacje. Od specjalistów
z tej dziedziny wymaga się wysokiego poziomu
świadomości technologii i doświadczenia, tak
by spełniali dla firmy rolę etycznego hakera.
Ścieżki kariery w testowaniu
Jedną z możliwości rozwoju zawodowego jest awans
w strukturze zespołu testowego firmy. Ścieżka może wyglądać
podobnie niezależnie od tego czy weźmiemy pod
uwagę zespół testów biznesowych czy technicznych.
Młodszy tester dostaje do wykonania prostsze przypadki
i pracuje pod okiem ekspertów. Ma świetną okazję,
aby zdobywać wiedzę o procesach realizowanych
w testowanych systemach. Poznaje metodykę testów,
stosowane procedury i wspierające narzędzia. Przyswaja
specyfikacje techniczne, uczy się nowych aplikacji
i poznaje model danych. Po przejściu kilka razy
przez cykl wdrożeniowy dostaje pod opiekę swoją
część wdrażanego zakresu zmian. Ma za zadanie zaprojektować
testy, skoordynować pracę przydzielonych
osób i w takim zespole przejść scenariusze testowe,
wykryć i zaraportować błędy. Ma dostarczyć kierownikowi
testów informacji z danego obszaru niezbędnych
do przedstawienia rekomendacji dla wdrożenia. Je-
TESTOWANIE OPROGRAMOWANIA
śli ktoś sprawdza się w tych zadaniach, ma dobre postrzeganie
celów projektu, w którym bierze udział, dobrze
komunikuje się z organizacją, a przede wszystkim
ze swoim zespołem, to jest postrzegany jako dobry kandydat
na kierownika testów.
Częstym przykładem możliwości rozwoju jest sytuacja,
w której projektant testów na tyle dobrze poznaje
testowane rozwiązanie, że powierza mu się analizę
wymagań i tworzenie projektu technicznego systemu.
Osoby, które w trakcie wykonywania testów świetnie radzą
sobie z całą grupą systemów są predysponowane
do roli architekta rozwiązania.
Korzystnym rozwiązaniem jest zdecydowanie się na
specjalizację. Rozwijamy wówczas warsztat technik
i narzędzi, które są wykorzystywane przy konkretnym
rodzaju testów. Osoby mające wiedzę z zakresu języków
programowania, czy administracji aplikacji będą
próbować sił w skryptach automatycznych, czy testowaniu
wydajności. Ci, którzy mają doświadczenie z administracją
czy przetwarzaniem danych wybiorą dla siebie
działkę testów migracji danych lub tematykę zasilania
środowisk w dane testowe. Generalnie, tą możliwość
częściej wybiorą osoby, które dobrze czują się
samodzielnie organizując sobie pracę, a niekoniecznie
chcą być liderami zespołów.
Część osób, w trakcie pełnienia funkcji lidera obszaru
testów, ujawnia zdolności menadżerskie i postanawia
iść w stronę kierowania projektami IT. Oczywiście
warsztat kierownika projektu jest na tyle szeroki, że nie
dzieje się to od razu i zwykle zaczyna się od wsparcia
doświadczonego kierownika projektu.
Podsumowanie
Testerem oprogramowania można zostać posiadając
zarówno wiedzę techniczną, jak i biznesową. Zależnie
od swojego profilu kompetencji można wybrać właściwą
dla siebie specjalizację. Można podążać wieloma ścieżkami
rozwoju zawodowego. Testowanie oprogramowania
jest dziedziną, w której każdy może znaleźć coś dla
siebie. Z pewnością nie jest to praca nudna. Wymaga
ciągłego podnoszenia kwalifikacji, dostarcza adrenaliny
i daje satysfakcję. Zachęcam do sprawdzenia możliwości,
jakie oferuje kariera w tym kierunku!
JACEK TOMCZAK
Ukończył Szkołę Główną Handlową na kierunku
�nanse i bankowość. W zawodzie pracuje
od 8 lat zajmując się organizacją i wykonywaniem
testów integracyjnych i akceptacyjnych.
Pracował jako kierownik zespołu testów w Telekomunikacji
Polskiej S.A. i Polskiej Telefonii
Cyfrowej Sp. z o.o. Aktualnie pełni funkcję Dyrektora Biura Projektów
w �rmie So�ab Technology Sp. z o.o., specjalizującej się
w testach oprogramowania. Zawodowo interesuje się dyscypliną
zapewnienia jakości w projektach informatycznych.
4/2011
" # $% #& ' ( ) " (
* " * # $ *& " ' " * # * % " # +
,- ! "
�
�
# # !
� $ %&' ()**
� $ +&, ()**
- ./0
�
�
!
!
1
WYWIAD
O automatyzacji usług IT i
realiach cloud computing
rozmowa z Gurem Steifem, gościem BMC Forum w Warszawie
Zacznijmy od automatyzacji usług IT. Wszyscy
wiedzą, że powinno się ją stosować, ale w
praktyce bywa z tym różnie... Automatyzacja
usług IT wydaje się być bardzo ważnym
czynnikiem wpływającym na sprawność i
ciągłość prowadzonego biznesu. Czy mógłby Pan
wyjaśnić naszym Czytelnikom na czym polega
taka automatyzacja i jakie korzyści przynosi jej
stosowanie?
Kiedy mówimy o automatyzacji usług IT, musimy wziąć
pod uwagę kilka różnych czynników. Pierwsze pytanie,
na które należy sobie odpowiedzieć musi brzmieć tak:
co właściwie chcemy zautomatyzować? Dlatego pierwszym
elementem każdej strategii automatyzacji jest
stworzenie katalogu usług. Jest to też dobra metoda zakomunikowania
użytkownikom końcowym, jakie usługi
są realizowane przez dział IT. Wówczas każde żądanie
ze strony użytkownika może zostać zrealizowane automatycznie.
Zwiększa się też jakość usług, kiedy znane
są oczekiwania i wiadomo jak i kiedy zostaną one
zrealizowane. Bez wdrożonej automatyzacji, żądania
użytkowników są nieprecyzyjne a ludzie realizujący je
dostarczają coś nie wiedząc czy to spełni oczekiwania.
Oczywiście automatyzacja przebiega stopniowo. Przechodzimy
do automatyzacji obciążeń. Zapewnia ona
bezbłędne wykonywanie tego procesu.
Więc jeśli twórca oprogramowania pisze aplikację,
powinien być zainteresowany tym, żeby jej użytkownik
był jak najbardziej zadowolony z jej użytkowania. Także
tym, czy procesy jej wdrażania czy zaopatrywania systemów
będą mogły być zautomatyzowane. Im więcej
automatyzacji, tym bardziej zadowoleni użytkownicy.
Oczywiście mówimy tutaj o automatyzacji usług biznesowych,
czyli tak naprawdę o relacji pomiędzy użytkownikiem
a działem IT, dbając o to, by IT rzeczywiście
obsługiwało potrzeby biznesowe. Załóżmy, że pracuję
w banku i przygotowałem jakąś nową promocję dla
klientów i oczekuję, że przyniesie ona efekt w postaci
większej liczby klientów dokonujących jakieś transakcje.
Muszę mieć pewność, że bank będzie w stanie
technicznie obsłużyć tych klientów. Chciałbym mieć
możliwość prostego zażądania tego od działu IT. Automatyzacja
usług IT sprawia, że automatycznie zostaną
przydzielone dodatkowe zasoby, które obsłużą nowych
klientów.
Jakie są wg Pana najczęściej występujące
negatywne czynniki hamujące w firmach IT
rozwój automatyzacji usług.
Utworzenie katalogu usług, o którym mówiłem wcześniej
nie zawsze jest łatwe. Następnie potrzebne są odpowiednie
rozwiązania i narzędzia umożliwiające automatyzację
tych usług. Proszę pamiętać, że nie możemy
oczekiwać od ludzi biznesu, że przyjdą i powiedzą: potrzebuję
trzy serwery z takim a takim procesorem, tyle
pamięci i tyle pojemności. Katalog usług jest w pewnym
sensie tłumaczem pomiędzy potrzebami biznesowymi
a tym, czym fascynują się informatycy. Automatyzacja
odbywa się w tle tego wszystkiego. System sam przydziela
dodatkowe zasoby w przypadku tej hipotetycznej
promocji marketingowej a potem sam je likwiduje, kiedy
wolumen transakcji wraca do poprzedniego poziomu.
Dlatego to pozwala nam także na lepsze wykorzystanie
fizycznych zasobów.
Najlepszym przykładem są maszyny, które w ciągu
dnia wykonują inne zadania, a w nocy są rekonfigurowane
do obsługi zupełnie innych rzeczy. Dlatego że
prawdopodobnie w ciągu dnia część ludzi przychodzi
do oddziału banku, a deweloperzy piszą kod i potrzebne
są inne zasoby niż w nocy, kiedy oddziały są
zamknięte a wolumen transakcji on-line także znacznie
spada, zaś przebiegają w banku wszystkie procesy
rozliczeniowe. Rekonfiguracja systemów IT powinna
odbywać się automatycznie, by zapewnić lepszy
poziom usług przy lepszym wykorzystaniu zasobów
sprzętowych. Daje to także dużo większe możliwości
szybkiego reagowania na zmieniającą się sytuację na
rynku.
Nie ma już żadnych barier technologicznych a rynek
bardzo dobrze przyjął takie rozwiązania. W ciągu
ostatnich kwartałów, tylko w samym cloud computing
wygenerowaliśmy obrót rzędu 100 mln USD. Nasze
doświadczenie pokazuje, że jeśli już klient zobaczy to
rozwiązanie w działaniu, to chyba nie zdarzyło się żeby
powiedział: nie, to nie dla mnie, czy coś w tym rodzaju.
Wartość automatyzacji jest tak oczywista.
Często spotykaną barierą ale już nie technologiczną
jest zbyt duża reaktywność w podejściu do informatyki.
Niektórzy myślą, że jeśli nic się złego nie dzieje w tym
momencie to nie trzeba nic zmieniać. To nie jest dobre
podejście.
01/2011
O automatyzacji usług IT i realiach cloud computing
Przejdźmy więc do cloud computing.
Przetwarzanie w chmurze to ostatnimi czasy
bardzo chwytliwy temat. Słyszy się różne, często
skrajne opinie, na ten temat. Jak Pan sądzi -
czy systemy działające w chmurze to jedynie
chwilowa moda, czy może stoimy u progu jakiejś
rewolucji w sektorze IT?
To bardzo dobre pytanie. Cloud jest ostatnio słowem
kluczowym w branży i mnóstwo się o tym mówi, przez
co każdy producent próbuje przekonywać że posiada
produkty przeznaczone dla chmur. Każda duża firma
próbuje przekonać klientów, że to właśnie ona posiada
najlepsze technologie w tej dziedzinie. Rzeczywiście,
może to być nieco frustrujące dla klientów, którzy nie
potrafią odróżnić prawdy od fikcji. Jednak cloud computing
rzeczywiście oferuje nowe możliwości, dzięki
którym biznes może lepiej wykorzystywać informatykę.
BMC jest w tej dobrej sytuacji, że my nikomu nie
próbujemy sprzedawać chmury. Nie oferujemy pakietu
sprzętu i oprogramowania, na którym można zbudować
chmurę. My tylko pomagamy w wykorzystaniu inwestycji,
które zostały już poniesione na infrastrukturę IT -
pomagamy naszym klientom w efektywnym zarządzaniu
środowiskiem, m.in. w chmurze. Nie jesteśmy też
związani z żadnym producentem sprzętu czy oprogramowania.
Jesteśmy zupełnie neutralni w zakresie platformy,
co pozwala nam oferować klientom rzeczywistą
wartość. My jesteśmy potrzebni tam, gdzie firma posiada
różne platformy i chce zastosować chmurę prywatną
lub publiczną czy jedno i drugie, gdzie część środowiska
jest fizyczna a część wirtualna. BMC dostarcza
rozwiązania do zarządzania tym wszystkim w jednolity
sposób.
Np. jeden z naszych klientów używa naszych rozwiązań
do automatyzacji procesów biznesowych. Raz w tygodniu
pojawia się potrzeba procesowania dużej liczby
transakcji. Zamiast konfigurować fizyczne serwery, które
używane by były wyłącznie podczas tego tygodniowego
szczytu, raz w tygodniu firma korzysta z chmury
publicznej, która dosłownie przez kilka godzin dostar-
sdjournal.org 2
3
cza dodatkowe zasoby. Firma płaci wyłącznie za ten
czas, w którym z chmury korzysta.
Zatem tak – potwierdzam, że cloud computing istnieje,
jest już z powodzeniem stosowany przez wiele firm
i rzeczywiście zmienia całą naszą branżę. Niestety nie
każdy producent używający tego określenia rzeczywiście
wie o czym mówi.
Na jaki rodzaj wsparcia mogą liczyć członkowie
zespołów IT, którzy wciąż obawiają się
stosowania chmur?
Krajobraz informatyczny a z nim cloud computing podlega
nieustannej ewolucji. Chmura to nie jest nasze
miejsce docelowe, tylko to jest podróż. Weszliśmy na tą
drogę i obserwujemy jak zmienia się to środowisko. Za
pięć lat może to wyglądać zupełnie inaczej.
BMC przez lata udowodniło, że jest w stanie pomagać
klientom w efektywnym wykorzystywaniu informatyki i
maksymalizowaniu korzyści z poczynionych w nią inwestycji
ale także dostosowywać rozwiązania do zmieniającej
się sytuacji. Jest wiele różnych strategii wykorzystywania
chmur i nie ma jednej właściwej odpowiedzi,
WYWIAD
która będzie pasowała do każdego biznesu. Powiem
więcej, niektóre firmy będą musiały wykorzystać więcej
niż jedną strategię. BMC sprawia, że klienci czują
się bezpieczniej w tej sytuacji, w której nie wiedzą, czy
podążają we właściwym kierunku. Niezależnie od tego,
na którą drogę się zdecyduję, my będziemy w stanie
zapewnić zarządzanie tym wszystkim jak jednym środowiskiem
IT.
O rozwiązaniach wykorzystujących możliwość
przetwarzania w chmurze słyszy się najczęściej w
kontekście wielkich korporacji bądź organizacji
rządowych. Jak Pan sądzi, kiedy rozwiązania tego
typu mogą trafić «pod strzechy» nieco mniejszych
firm?
Przetwarzanie w chmurze jest potencjalnie ogromną
szansą także dla mniejszych firm. Niektóre usługi, z których
teraz korzystają wewnątrz, mogą być przeniesione
do chmury. Czasem nawet dla małej firmy jest to łatwiejsze
rozwiązanie niż dla dużej. Jeżeli firma używa np. 2
serwerów i potrzebuje trzeci, to w bardzo łatwy sposób
może potrzebne zasoby otrzymać z chmury publicznej
zamiast kupować trzeci serwer i płacić za jego utrzymanie.
To oczywiście powoduje ogromny popyt na rynku
dostawców usług. Z kolei jako ten dostawca usług,
nie będę w stanie zapewnić odpowiedniego poziomu
usług dużej liczbie małych klientów, jeśli nie będę miał
odpowiedniego rozwiązania do efektywnego zarządzania
chmurą, do zaopatrywania klientów, itd. Właśnie do
tego rodzaju zastosowań stworzony został BMC Cloud
Management System.
To też jest element tej transformacji, jaki cloud computing
wywołuje na rynku.
Tutaj pojawia się zawsze problem z
bezpieczeństwem i zgodnością z regulacjami.
Tak, w tej branży zawsze jest wiele pytań i wątpliwości
ale przecież są one rozwiązywane. Znamy wielkie firmy
finansowe, które już dziś korzystają z chmur w zakresie
wykonywania transakcji.
Fragmentacja na rynku platform ciągle rośnie.
Razem z nią rośnie złożoność budowanych
systemów. Integracja międzyplatformowa od
zawsze była jednym z największych praktycznych
wyzwań w sektorze IT. Jak według Pana
zagadnienie to wypływa na rozwój branży IT?
Niewątpliwie jest to problem z jakim wciąż borykają się
klienci. To właśnie rosnąca złożoność środowisk IT sprawia,
że potrzebna jest automatyzacja. Nasza przewaga
polega na tym, że potrafimy automatyzować usługi IT
niezależnie od platform, a nawet tam, gdzie w jednym
środowisku stosowane są różne platformy. W pewnym
sensie chmura jest też lekarstwem na te bolączki, pod
warunkiem, że jest odpowiednio zarządzana.
01/2011
����������������������������
����������
������������������������
�����������������
����������������������������������������������
������������������������������������������������
������������������������������������������������
������������������������������������������������
������������������������������������������������
�������������������������������������������������
���������������������������������������������
�������������������������������������������
���������������������������������
��������������������������������������������
�������������������������������������������
������������������������������������
� ������������������������������������
72
WYWIAD
Oczekiwania zależą
od projektu
Wywiad z Dominiką Kotułą, menagerem personalnym
w firmie Blue Media S.A.
Rekrutuje Pani pracowników na różne
stanowiska. Jakich pracowników szukacie
najczęściej?
Dla firmy Blue Media priorytetem jest obecnie dalszy rozwój
kluczowych produktów. Szukając osób, które pomogą
nam realizować ten cel, zawsze najpierw określam
z członkami poszczególnych zespołów kryteria, które powinien
spełniać kandydat. Praca w każdym projekcie jest
nieco inna, więc szukamy różnych osób. Dla przykładu:
zespół marketu ubezpieczeniowego Inseco.pl składa
się z pracowników doświadczonych w branży finansowo
– ubezpieczeniowej. Do tej grupy szukamy przede wszystkim
ludzi posiadających umiejętność tworzenia, integrowania,
motywowania oraz organizowania zespołów. Myślę
o osobach, które potrafią analizować fakty, zachodzące
na rynku zmiany i na tej podstawie przewidywać przyszłe
trendy, formułować strategie działań. Zupełnie innym projektem
jest portal pożyczek społecznościowych Kokos.pl.
Tu z kolei liczy się umiejętność generowania niekonwencjonalnych,
oryginalnych rozwiązań, znajdujących zastosowanie
w praktyce. Przydaje się też otwartość na zmiany
i szybkie się do nich dostosowywanie. Natomiast do naszego
zespołu pracującego nad serwisem szybkich przelewów
międzybankowych BlueCash.pl zapraszamy osoby,
które realizując zadania, w sposób efektywny potrafią
zarządzać czasem i pracować pod jego presją. Takie oso-
Blue Media S.A.
Firma funkcjonuje na rynku informatycznym już 12 lat. Głównymi
odbiorcami oferowanych usług są największe banki działające
na polskim rynku oraz wiodące towarzystwa ubezpieczeniowe.
Innowacyjność, atrakcyjny model biznesowy oraz
technologiczne zaawansowanie oferowanych usług to atuty
Blue Media na rynku nowoczesnych systemów transakcyjnych.
W trakcie 12 letniego funkcjonowania Blue Media otrzymała
szereg nagród i wyróżnień. Trzy razy pod rząd, w roku 2006,
2007 i 2008 zajęła 1 miejsce w rankingu Technology Fast50
grupującym spółki technologiczne z Europy Środkowej. Dwa
razy otrzymała tytuł Gazel Biznesu, w 2007 i 2010 roku oraz
miano Najlepszy Pracodawca 2008 i 2009. W zestawieniu Computerworld
TOP200 Blue Media jest klasyfikowana jako największa
firma informatyczna na Pomorzu.
Spółka aktywnie włącza się w inicjatywy na rzecz rozwoju nowoczesnych
rozwiązań technologicznych współpracując z Forum
Technologii Bankowych przy Związku Banków Polskich
oraz z Polską Izbą Informatyki i Telekomunikacji.
by muszą potrafić szybko reagować na zaistniałe problemy
i wyszukiwać możliwości właściwego ich rozwiązania.
Jak przebiega rekrutacja do tych zespołów?
Zasady rekrutacji obowiązujące w firmie Blue Media są
kompromisem wynikającym z dwóch podstawowych założeń:
oszczędności czasu, tak aplikanta jak i pracowników
Spółki oraz możliwości jak najcelniejszej oceny kwalifikacji
osoby ubiegającej się o pracę na danym stanowisku.
W przyjętym modelu rekrutacja przebiega w trzech etapach.
Wszystkie mają formę rozmowy kwalifikacyjnej połączonej
często z merytorycznym sprawdzeniem wiedzy
poprzez testy lub rozwiązanie zagadnień problemowych.
Stosowana jest również forma „zadań domowych”, które
pozwalają sprawdzić umiejętności kandydatów w mniej
stresujących warunkach.
Co oferujecie kandydatom w zamian za
realizację postawionych celów?
Przede wszystkim pracę przy współtworzeniu innowacyjnych
projektów oraz możliwość podnoszenia kwalifikacji
i rozwoju zawodowego. Nie bez znaczenia jest fakt,
że praca w Blue Media to ciekawe, pełne wyzwań zajęcie
w prestiżowej firmie, której siedziba mieści się w ścisłym
centrum Sopotu. Pracownikom zapewniamy też relaks
organizując m.in. integracyjne zajęcia sportowe. Pracując
u nas trzeba wiedzieć, że, bardziej niż czas poświęcony
na realizację powierzonych zadań liczy się dla nas
efekt pracy. Potwierdzeniem tego jest fakt, że prawie każdy
pracownik ma możliwość indywidualnego ustalenia godzin
pracy, aby skutecznie realizować swoje obowiązki.
W niektórych sytuacjach oferujemy możliwość pracy zdalnej
(np. rodzicom).
DOMINIKA KOTUŁA
Od 6 lat zawodowo związana
z Blue Media S.A. Jako HR
Manager zajmuje się m.in.
rekrutacją i selekcją oraz
rozwojem pracowników, odpowiada
za budowanie kultury organizacyjnej. Absolwentka
Uniwersytetu Gdańskiego i Politechniki Gdańskiej, interesuje
się zagadnieniami dotyczącymi ZZL oraz wpływem ADHD u dorosłych
na życie zawodowe.
4/2011
����������������������������������������������
�������������������������������������������
��������������������������
������� �� ���� ����������� ��� �������� ����������
���������������������������������������������������
�����������������������������������������������������
������������������������������������������������������
����������� ������� ������� ����� ����� ������ ��������
������������������������������������������������������
������������ �������� ������� ��������� ���� ��������
�����������������������������������������������������
������� ������������ ������������ ����� ���� ���������
����������������������������������������������������
��������������������������������������������������
����������������
���������� ������������� �� � ���� ����������� �����
�������������������������������������������������
�����������������������������������������������������
����������� ����� �� ��������� ������� ��� ��������������
����������
���� ���������� ������� �� ���� ����������� ������ ���
���������� ������������� ������������� ��� �� �������
������������������
�������������������
����������������������������������������������
� ������������
� ���������������
� �����������
� ��������
� ������������
� ����������������
� ���������������������������������
� ����������������������
� ������������������������������
� ������������
� ��������
� ���������������������������������
�����������������������������������������������������
�����������������������������������������������������
�������������������������������
���������������������������������������������������
����������������������������������������������������
�������������������
����������������������
������������
������������������
��������������
������������������
����������������������������������
�������������������
�����������������������
�������������������������������������������������������������
�������� �������� ��� ��������
��������������� �����������
�� ��������� ���������� ����
�������������������������������
�������������������������������
�����������������������������������
������ ����������� �����������
��� ����������� �� ������ ����� ��������
���������������������
������ ������� �� �������������� ���
������������������������������������
���� ������������� ��������� �������
������� ������������ ������������
�����������������������������������
���������� ���������� �� �������
�������� �������� ������� ���������
������� ���������� ������ ����������
�������� ������������ ���� ������
��� ��������� ���� � ���� ������� �� ���
���� ��������� ���������� ���������
����������������� ������������������
��������������������������������
������������
���������������������������������
������������������������������������
������������������������������������
��������������������������������
��������� ���� ������� ������������
������� ����� �����������������
������ ����������� ������ ����������
�� �������� ������������ ������������
����������������������������������
����������������������������������
����������������������������
�������� ��������� ���������� ������
�������� ��� ���������������� ����
����� ��� �������������� ��������
���������������������������������
����� ������������� ��������������
��������� ������� ������ ����������
������� �������� ���������� �������
������������������������������� ���
��������� ������� �� ����� ���������
�����������
�����������������������������������
������������
����� ������������� ������������
� ���������������������������������
���� ������������� ��������� ������
������ ���� ��� ��� ����� �� ������ ������
����������������������������������
���������������������������������
���������� �� ������������ ��������
� ��������� ���� ������� ����������
�������������������������������
�����������������������������������
���� ���������� ����������� �� ������
�����������������
����������������
��������������
��������� ������
��������������
���� �����
����������
���������� ���� ������ ���������� ������
���������� ������� ���� ������ ������
������� �� �������� ��� ������ ��������
�������� ��� �������� ������� ���� �����
��� ������������ ��� ����� ��� ����������
� ���������������������������������
� ������������� ���� ����� ������� ����
�����������������������������������
����������� �������� ���� ����� ����
������ ����������� ���� ��� ���������
������������ ������������ ��������
��������� ���������� ��������������
������������ ����������� �����������
��������� �������������� �������
����
������������������������������������
�����������������������������������
�����������������������������������
�������� ������ ��������� ������������
� ��������� ����������� ����������
��������� ��� �������� ������ �������
����������������������������������
�������������������������������������
������������������������� ������
���������������������������������
�������������������������������������
������������������������������������
���� �������� ��������� ������� ����
���������
������������ �������������� �������
�� �������� �������� ��������� �� ����
������������������������������������
���������������������������������
������������������������������������
��������� �� ������������� ���� �����
������� ��� ��������� ��������� �����
�������������������� ��� �������
������������������������������������
���������������������������������
����������������������������������
����������������������������������
������������������������������������
�����������������������������������
��������� ����� �������� ��� �������
�����������������������
������������������������
�����������������������������������
����� ���������� ��� ���� �����������
������������������������������������
����� ������� ��������� �������� ������
������������������������������������
���������� ����� ���� ��� ������� �� ���
��������������
����������������
�������������������������������������
�������� ���������� ��������� �� ������
������� ����� ������������ ������ ��������
������ ��������������� �� ����������
��������� ������� ������� ����� ��� ��
���������������� ������� ���������� ������
�������������������������������������
��������� ������������������� ���� ����
����� ����������� ��������� �� ������ ������
�������������������������������������
�������������������������������������
����������������������������������������
���������������� �� ����������������
�������������������������������������
������������������������������������
������������������������������������
������������������������
�����������������������������������
�������������������
�������������������������
����������
�������������������
�����������������������������
������� ������� ���������� ���
���������� �������������� ���
������ ���������� ������������
�����������������������������������
�����������������������
�������������������
���������������������������
��������������������������������
��������������������������������������
�����������������������������������
�������������������������������������
��������������� ����������� ������
�������������� ��� ��������� �����
��������
������� ������� ����� �� �������������
�������� �������� ��������� �����
��� �� ����������� �������� ����������
� ���������
�������������� �������� ����������
�����������������������������������
������� ������������ ����� ����������
���������� ��� ���������� �����������
�������� �������������� ����������
����������������������������������
����������� ���������� �������������
��������� ���� ������������ ��������
������ ���������� ����� ��������� ���
�����������������������������������
����������
������������������������������������
�����������������������������������
�������� ������������� ������ ������
����
����������������������
�����������������������������
������������������������������
��������������������������������
�������������������
���������������������������
����������������������������
�������������������������������
������������
�������
������������
� �������������
��������
��������
���������
�����������
����������������������������������
������� ��������� ��� ���������� ����
����������������������������������
�������������������������������������
�������������������������������
�������������������
����������
���������
��������
����������
����������
��������
������
�����
������������������
�����
������
�����
�������
���������������������������
��������������
����������
��������������
��������
�������
��������
�����������
��������
�����������������
�������
������� �������� �������� ����������
���������� ��������������� ������
��������� ������������� ���������
�������� �� ��������� ��������������
��������������������������������������
���������������������
������ �� ������������ ������� �����
���������� ���� ������� ����������
����������� ��� ��������� ���������
������� �������� ������������� �������
������������ ������ �������� �� ������
���������� ������ ����� ���������
������� ��������� ������� ����������
���������� ���������� ����� �����
����������� ��������������� ��������
�������������������������
����������������������
������������������� ������������
�����
���������������������������
����������
�����������������������������������������������������
������������������
��������������������������������������������������
������������������������������������������������
������������������������������������������������
����������������
��������������������������������������������
����������
�������������������
�������������������������������������
�����������������������������������������
���������������������
���������������
��������������������������������
��������������������������������������������
���������������������������
���������������������
����������������������������
����������������������
��������������������������������������
������������������������
���������������������������������������
� ����������������������������������������������
����������������������
��������������������������
����������������������������
������������������������������������������
� ��������������������������������
�����������������������������
������������������������������������������
� ���������������������������������
� ������������������������� ������������������
�����������
�����������������
��������������������������������������
���������������������
������������� �������� ���������� �������� ���� �� ���
��������� �� ������ ������������� ����� ������� ��������
����������
�������������������������������������������������������
��������������������������������������������
������������
��������������
�������������������������������������
��������������������������������������