sztuczna inteligencja - Software Developer's Journal

sdjournal.pl

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


����������������������������������������������

�������������������������������������������

��������������������������

������� �� ���� ����������� ��� �������� ����������

���������������������������������������������������

�����������������������������������������������������

������������������������������������������������������

����������� ������� ������� ����� ����� ������ ��������

������������������������������������������������������

������������ �������� ������� ��������� ���� ��������

�����������������������������������������������������

������� ������������ ������������ ����� ���� ���������

����������������������������������������������������

��������������������������������������������������

����������������

���������� ������������� �� � ���� ����������� �����

�������������������������������������������������

�����������������������������������������������������

����������� ����� �� ��������� ������� ��� ��������������

����������

���� ���������� ������� �� ���� ����������� ������ ���

���������� ������������� ������������� ��� �� �������

������������������

�������������������

����������������������������������������������

� ������������

� ���������������

� �����������

� ��������

� ������������

� ����������������

� ���������������������������������

� ����������������������

� ������������������������������

� ������������

� ��������

� ���������������������������������

�����������������������������������������������������

�����������������������������������������������������

�������������������������������

���������������������������������������������������

����������������������������������������������������

�������������������

����������������������

������������

������������������

��������������

������������������

����������������������������������


�������������������

�����������������������

�������������������������������������������������������������

�������� �������� ��� ��������

��������������� �����������

�� ��������� ���������� ����

�������������������������������

�������������������������������

�����������������������������������

������ ����������� �����������

��� ����������� �� ������ ����� ��������

���������������������

������ ������� �� �������������� ���

������������������������������������

���� ������������� ��������� �������

������� ������������ ������������

�����������������������������������

���������� ���������� �� �������

�������� �������� ������� ���������

������� ���������� ������ ����������

�������� ������������ ���� ������

��� ��������� ���� � ���� ������� �� ���

���� ��������� ���������� ���������

����������������� ������������������

��������������������������������

������������

���������������������������������

������������������������������������

������������������������������������

��������������������������������

��������� ���� ������� ������������

������� ����� �����������������

������ ����������� ������ ����������

�� �������� ������������ ������������

����������������������������������

����������������������������������

����������������������������

�������� ��������� ���������� ������

�������� ��� ���������������� ����

����� ��� �������������� ��������

���������������������������������

����� ������������� ��������������

��������� ������� ������ ����������

������� �������� ���������� �������

������������������������������� ���

��������� ������� �� ����� ���������

�����������

�����������������������������������

������������

����� ������������� ������������

� ���������������������������������

���� ������������� ��������� ������

������ ���� ��� ��� ����� �� ������ ������

����������������������������������

���������������������������������

���������� �� ������������ ��������

� ��������� ���� ������� ����������

�������������������������������

�����������������������������������

���� ���������� ����������� �� ������

�����������������

����������������

��������������

��������� ������

��������������

���� �����

����������

���������� ���� ������ ���������� ������

���������� ������� ���� ������ ������

������� �� �������� ��� ������ ��������

�������� ��� �������� ������� ���� �����

��� ������������ ��� ����� ��� ����������

� ���������������������������������

� ������������� ���� ����� ������� ����

�����������������������������������

����������� �������� ���� ����� ����

������ ����������� ���� ��� ���������

������������ ������������ ��������

��������� ���������� ��������������

������������ ����������� �����������

��������� �������������� �������

����

������������������������������������

�����������������������������������

�����������������������������������

�������� ������ ��������� ������������

� ��������� ����������� ����������

��������� ��� �������� ������ �������

����������������������������������

�������������������������������������

������������������������� ������

���������������������������������

�������������������������������������

������������������������������������

���� �������� ��������� ������� ����

���������

������������ �������������� �������

�� �������� �������� ��������� �� ����

������������������������������������

���������������������������������

������������������������������������

��������� �� ������������� ���� �����

������� ��� ��������� ��������� �����

�������������������� ��� �������

������������������������������������

���������������������������������

����������������������������������

����������������������������������

������������������������������������

�����������������������������������

��������� ����� �������� ��� �������

�����������������������

������������������������

�����������������������������������

����� ���������� ��� ���� �����������

������������������������������������

����� ������� ��������� �������� ������

������������������������������������

���������� ����� ���� ��� ������� �� ���

��������������

����������������

�������������������������������������

�������� ���������� ��������� �� ������

������� ����� ������������ ������ ��������

������ ��������������� �� ����������

��������� ������� ������� ����� ��� ��

���������������� ������� ���������� ������

�������������������������������������

��������� ������������������� ���� ����

����� ����������� ��������� �� ������ ������

�������������������������������������

�������������������������������������

����������������������������������������

���������������� �� ����������������

�������������������������������������

������������������������������������

������������������������������������

������������������������

�����������������������������������

�������������������


�������������������������

����������

�������������������

�����������������������������

������� ������� ���������� ���

���������� �������������� ���

������ ���������� ������������

�����������������������������������

�����������������������

�������������������

���������������������������

��������������������������������

��������������������������������������

�����������������������������������

�������������������������������������

��������������� ����������� ������

�������������� ��� ��������� �����

��������

������� ������� ����� �� �������������

�������� �������� ��������� �����

��� �� ����������� �������� ����������

� ���������

�������������� �������� ����������

�����������������������������������

������� ������������ ����� ����������

���������� ��� ���������� �����������

�������� �������������� ����������

����������������������������������

����������� ���������� �������������

��������� ���� ������������ ��������

������ ���������� ����� ��������� ���

�����������������������������������

����������

������������������������������������

�����������������������������������

�������� ������������� ������ ������

����

����������������������

�����������������������������

������������������������������

��������������������������������

�������������������

���������������������������

����������������������������

�������������������������������

������������

�������

������������

� �������������

��������

��������

���������

�����������

����������������������������������

������� ��������� ��� ���������� ����

����������������������������������

�������������������������������������

�������������������������������

�������������������

����������

���������

��������

����������

����������

��������

������

�����

������������������

�����

������

�����

�������

���������������������������

��������������

����������

��������������

��������

�������

��������

�����������

��������

�����������������

�������

������� �������� �������� ����������

���������� ��������������� ������

��������� ������������� ���������

�������� �� ��������� ��������������

��������������������������������������

���������������������

������ �� ������������ ������� �����

���������� ���� ������� ����������

����������� ��� ��������� ���������

������� �������� ������������� �������

������������ ������ �������� �� ������

���������� ������ ����� ���������

������� ��������� ������� ����������

���������� ���������� ����� �����

����������� ��������������� ��������

�������������������������

����������������������

������������������� ������������


�����

���������������������������

����������

�����������������������������������������������������

������������������

��������������������������������������������������

������������������������������������������������

������������������������������������������������

����������������

��������������������������������������������

����������

�������������������

�������������������������������������

�����������������������������������������

���������������������

���������������

��������������������������������

��������������������������������������������

���������������������������

���������������������

����������������������������

����������������������

��������������������������������������

������������������������

���������������������������������������

� ����������������������������������������������

����������������������

��������������������������

����������������������������

������������������������������������������

� ��������������������������������

�����������������������������

������������������������������������������

� ���������������������������������

� ������������������������� ������������������

�����������

�����������������

��������������������������������������

���������������������

������������� �������� ���������� �������� ���� �� ���

��������� �� ������ ������������� ����� ������� ��������

����������

�������������������������������������������������������

��������������������������������������������

������������

��������������

�������������������������������������

��������������������������������������

Similar magazines