งาน, กำลัง, ความซับซ้อน และ Story points เพื่อการประมาณ และ วางแผนงาน แบบ Agile

สวัสดีครับ

วันนี้ผมจะมาอธิบายการประมาณค่า เพื่อ วางแผนงานแบบ Agile ที่เรานิยมใช้กัน ก็คือ Story points (SP)

SP ก็คือ ขนาดของงาน หรือ ความต้องการ มันก็คือขนาดของซอฟแวร์ที่เรากำลังพัฒนา

เราใช้ SP เพื่อไปวางแผนปล่อยซอฟแวร์, คำนวนต้นทุนค่าแรงพัฒนาซอฟแวร์ และ ติดตามงานเพื่อให้อยู่ในแผนงานที่เรากำลังทำอยู่

เพราะว่า SP คือ ขนาดของงาน เราจึงสามารถนำงานต่างๆ มาเปลียบเทียบกันได้ โดยไม่ขึ้นกับจำนวนคน หรือความสามารถของคนที่จะมาทำ หรือ ความซับซ้อน ก็คือ ความไม่รู้ของคนที่จะทำงานชิ้นนี้ให้สำเร็จ

ส่วนความสามารถของคน หรือ ทีมที่จะทำงานนี้ให้เสร็จนั้น เรานิยมวัดด้วยขนาดของเวลาที่ใช้ทำงานนั้นจนเสร็จ เรียกว่า ความพยายาม (effort)

ในการอธิบายเรื่อง SP นี้ที่เราพบเจอกันบ่อย เขามักจะอธิบายว่า มันคือค่า ความพยายาม กับ ความซับซ้อนของงาน ซึ่งกำหนดแบบนี้ก็ไม่ผิด เดี๋ยวผมจะอธิบายต่อไปว่า SP, ความพยายาม และ ความซับซ้อนของงาน มันแตกต่างกันอย่างไร แล้วความแตกต่างกันตรงนี้ทำให้ SP นั้นเกิดความผิดพลาด (error) ในการวางแผนงานกันในทีมได้อย่างไร

ทำไม SP ถึงถูกอธิบายเป็นแบบนั้น ผมขออธิบายด้วย สมการงาน ครับ สมการงาน กำหนดได้แบบนี้

Work(W) = Force(F) x Distance(d)

Work ก็คือ งาน ซึ่งเทียบได้กับ ความต้องการ หรือ คุณสมบัติของซอฟแวร์ที่ต้องการ (feature) หรือ issue

Force ก็คือ กำลัง ซึ่งเทียบได้กับ ความพยายาม ที่คนในทีมใช้ทำงานนั้นจนสำเร็จ และ

Distance ก็คือ ระยะทาง ซึ่งเทียบได้กับขนาดของงาน หรือ SP

ตัวอย่าง เมื่อเราประมาณงานหนึ่งชิ้น (Work item) สมมิว่า คือ w1

เรามีทีมพัฒนาที่มีสมาชิกอยู่ 2 คน คือ p1 และ p2 ให้ทั้งสองคนประมาณงาน w1 นี้

p1 ประมาณ ใช้เวลา 1 วัน และ p2 ประมาณ ใช้เวลา 2 วัน

ถ้าเราใช้ กำลังเป็นหน่วยวัดของงานแล้ว เราจะพบว่า งานแบบเดียวกันขนาดมันไม่มีทางเท่ากันได้เลย เป็นเพราะอะไร

แทนค่าประมาณงานของทั้งสองคนลงไป ในสมการงาน เราจะได้

w1 = F * 1 — สำหรับ p1

w1 = F * 2 — สำหรับ p2

เราจะพบว่า การทำให้ w1 เหมือนกันให้ได้นั้น p1 ต้องออกแรง (F) มากกว่า p2 เป็น 2 เท่า แต่การที่เราเอาเวลา มาเป็นตัววัดขนาดงาน ให้แต่ละคนในทีมเห็นเหมือนกันนั้นเป็นไปไม่ได้เลย ดังนั้น จึงไม่แปลกว่าทำไมการจัดการวางแผนมีความผิดพลาด (error) อยู่เสมอ เช่น ปัญหาในการวางแผนงานโดยประมาณแบบผู้เชี่ยวชาญ ว่างานชิ้นนี้ คนนี้ใช้ 1 วันทำเสร็จ ดังนั้น งานชิ้นนี้ให้ใครทำก็จะต้อง 1 วันเสร็จเหมือนกัน ซึ่งเราจะเห็นว่า มันเป็นไปไม่ได้ในทุกกรณี หรือ ความเข้าใจที่ว่า งานชิ้นนี้คนเดียวทำ 1 วัน ถ้าเราใช้ 2 คนก็ต้องเสร็จได้ภายในครึ่งวันแน่นอน ความเข้าใจทั้งหมดนี้เกิดมากจาก การใช้ Effort กับ Complexity ประมาณขนาดของงาน ซึ่งทั้งสองค่านี้มีความสัมพันธ์กับทีมพัฒนาแต่ละคน ผมก็จะอธิบายในลำดับต่อไป

ดังนั้การจัดการแบบ Agile จึงเลือกใช้ SP มาเป็นตัวประมาณค่าขนาดของงาน เพื่อนำมาวางแผนงานทำงานต่อไป นั่นเอง

เทคนิคที่นิยมใช้ประมาณขนาดของงาน เช่น Planning poker, Swimlane Sizing

พอเราได้ขนาดมาแล้ว เราก็ให้ทีม มาประมาณกำลัง หรือเวลาทำงานชิ้นนั้นอีกที

ทีนี้มาถึงความซับซ้อนของงาน ทำไมมีหลายๆคน อธิบายว่า SP คือ ความพยายาม กับ ความซับซ้อน ได้

ผมจะยกตัวอย่าง งานเหมือนเดิม ในทีมพัฒนา มีสมาชิก 2 คนคือ p1 และ p2 ให้ประมาณงาน w1 เหมือนกัน ได้ดังนี้

p1 ประมาณงาน w1 ได้ 1 วัน และ p2 ก็ประมาณงาน w1 ได้ 2 วัน

w1 = F * 1 — สำหรับ p1

w1 = F * 2 — สำหรับ p2

จากสมการงาน เรารู้แล้วว่า F ของ p1 ไม่เท่ากับ p2 แต่ทำไมถึงไม่เท่ากัน และทำอย่างไรถึงจะให้เท่ากันได้ เพื่อให้เห็นขนาดของงาน w1 นี้ตรงกัน

ทีนี้ผมจะพยายามทำให้ ทั้งสองคนนี้ใช้กำลังเท่ากัน เพื่อใช้อธิบายเรื่องความซับซ้อนของงาน โดยเลือกเอาค่าน้องที่สุด เพื่อให้งานเสร็จไวครับ ก็คือ 1 วัน

ถ้า ผมให้ F เท่ากับ Effort (e) คือ ความพยายาม คูณกับ Knowledge (k) ความรู้ในงาน จะได้สมการงานใหม่ เป็น

F = k x e

นำไปแทนค่าใน สมการงาน ได้เป็น

W = k x e x d

โดยที่ k มีค่าระหว่าง 0 ถึง 1 คือ

ถ้าค่า k เท่ากับ 0 ไม่รู้เรื่องเกี่ยวกับงานนั้นเลย, ถ้าค่า k เท่ากับ 1 คือมีความรู้ในงานนั้นอย่างสมบูรณ์ ถ้าค่า k อยู่ระหว่าง 0 กับ 1 ก็คือ มีความรู้ในงานตามส่วนไป เช่น 0.5 หรือ รู้ 50% เป็นต้น

ถ้าผมให้ Complexity (c) หรือ ความซับซ้อน คือความไม่รู้เกี่ยวกับงาน ดังนั้น c จะเท่ากับ 1 – k นั่นเอง

ดังนั้น ถ้าผมให้ p1 มี k เท่ากับ 1 คือ มีความรู้สมบูรณ์ และ p2 มี k เท่ากับ 0.5 หรือรู้ในงานนี้ 50% แทนค่าในสมการงาน ได้เป็น

w1 = e * 1 * 1 — สำหรับ p1

w1 = e * 0.5 * 2 — สำหรับ p2

เราจะพบว่าสมการงาน มีค่าเท่ากันได้ โดย p1 และ p2 ในความพยายาม (e) เท่ากัน แต่ความรู้ของทั้ง p1 และ p2 ไม่เท่ากัน

และนี่ก็คือที่มาว่า ทำไมถึงมีคนอธิบายว่า Story points ที่ใช้เป็นหน่วยวัดงาน นั้น มันก็คือ ความพยายาม (e) กับ ความซับซ้อน (c) เพราะว่า พวกมันมีความสัมพันธ์กัน แต่ไม่เกี่ยวข้องกัน แต่ส่งผลทำให้เกิดงาน นั้นเอง

ก่อนไป ผมมีอีกสามาการหนึ่งที่สามารถนำมาใช้อธิบายความสัมพันธ์ระหว่าง งาน, กำลัง, ความซับซ้อน และ Story points ก็คือ สมการมูลค่า ที่ว่า

Value = Price x Quantity

ผมเทียบกับงานซอฟแวร์ แล้วอธิบายได้ว่า

Price เท่ากับ Effort x Knowledge และ Quantity ก็คือ Story points ก็ได้เช่นเดียวกับ สมการงาน ครับ

ผมก็ขออธิบายไว้เพียงเท่านี้ หวังว่าจะช่วยให้เข้าใจ Story points เพื่อใช้ในประมาณค่า เพื่อวางแผนงาน ที่เข้าใจได้ชัดเจนขึ้นต่อไปครับ

ขอบคุณครับ

#:P

หนังสือแนะนำอ่านเพิ่มเติม

 

 

Advertisements

สร้าง chart ด้วย d3js กับข้อมูลธนาคารแห่งประเทศไทย ผ่าน Web API อย่างง่าย

สวัสดีครับ

บล็อคนี้ ผมมานำเสนอ การสร้าง chart เพื่อแสดงข้อมูลทางเศรษฐกิจ ที่ได้จาก Web API ของ ธนาคารแห่งประเทศไทย (BOT) ที่เปิดให้ใช้ครับ

ตอนนี้บริการ Web API ที่เปิดให้ มีข้อมูลเกี่ยวกับ อัตราแลกเปลี่ยน, อัตราดอกเบี้ย และ ผลการประมูลตราสารหนี้ อ่านรายละเีอยด ได้ที่นี่ครับ https://iapi.bot.or.th/Developer?lang=th

ตัวอย่าง ที่ผมจะแสดง คือ เป็นแอพ Web แสดง line chart หรือ กราฟเส้น ของข้อมูล อัตราแลกเปลี่ยนเฉลี่ย (รายวัน) แบบนี้

แกนนอน แสดงเวลา แกนตั้งแสดง อัตราแลกเปลี่ยน บาท THB ต่อ ดอลล่าสหรัส USD และ ที่ text box ให้ผู้ใช้ใส่ ปี และ เดือน ลงไป พอกด select ก็จะส่ง ปี และ เดือน ไปเอาข้อมูลจาก BOT Web API มาแล้วแสดงผล เป็น line chart ตามรูปครับ

เริ่มสร้างกันเลย

1. สร้าง html file ขึ้นมาครับ ตั้งชื่อว่า DAILY_AVG_EXG_RATE.html เติม code html เริ่มต้นเข้าไปก่อน แบบนี้ครับ

อธิบาย ดูกล่อง สีเขียว ครับ คือผมเพิ่ม library jQuery สำหรับช่วย query element DOM html ให้ สะดวกดี และ d3js  ใช้สำหรับสร้าง chart แสดงผลข้อมูล

2. เพิ่ม style sheet สำหรับ แสดงเส้น grid บน chart เพื่อให้อ่าน chart ได้ง่ายขึ้น เติม code CSS นี้ต่อจาก script ครับ

3. เพิ่ม javascript file ครับตั้งชื่อ BotChartPlayer.js เพิ่ม code สำหรับ สร้าง chart และ เรียกข้อมูลผ่าน Web API ของแบงค์ชาติครับ แบบนี้

// BotChartPlayer.js
function BotChartPlayer(opts) {

opts = opts || {};
opts.elementID = opts.elementID || ‘chart’;
opts.width = opts.width || 1000;
opts.height = opts.height || 450;
opts.dateFormat = opts.dateFormat || ‘%Y-%m-%d’;
opts.x_column = opts.x_column || ‘date’;
opts.y_column = opts.y_column || ‘value’;
opts.uri = opts.uri || ‘https://iapi.bot.or.th/Stat/Stat-ExchangeRate/DAILY_AVG_EXG_RATE_V1’;

var dataSet = {};

var svg = d3.select(‘#’ + opts.elementID)
.append(“svg”)
.attr(“width”, opts.width)
.attr(“height”, opts.height),
margin = { top: 20, right: 20, bottom: 30, left: 50 },
width = +svg.attr(“width”) – margin.left – margin.right,
height = +svg.attr(“height”) – margin.top – margin.bottom,
g = svg.append(“g”).attr(“transform”, “translate(” + margin.left + “,” + margin.top + “)”);

var parseTime = d3.timeParse(opts.dateFormat),

x = d3.scaleTime().rangeRound([0, width]),

y = d3.scaleLinear().rangeRound([height, 0]);

var line = d3.line()
.x(function (d) { return x(parseTime(d[opts.x_column])); })
.y(function (d) { return y(+d[opts.y_column]); });

g.append(“g”)
.attr(“class”, “axis-bottom grid”);

g.append(“g”)
.attr(“class”, “axis-left grid”)
.append(“text”)
.attr(“fill”, “#000”)
.attr(“transform”, “rotate(-90)”)
.attr(“y”, 6)
.attr(“dy”, “0.71em”)
.attr(“text-anchor”, “end”)
.text(“THB / USD”);

g.append(“path”)
.attr(“class”, “line”)
.attr(“fill”, “none”)
.attr(“stroke”, “steelblue”)
.attr(“stroke-linejoin”, “round”)
.attr(“stroke-linecap”, “round”)
.attr(“stroke-width”, 1.5);

var display = function (data) {

x.domain(d3.extent(data, function (d) {

return parseTime(d[opts.x_column]);

}

));
y.domain(d3.extent(data, function (d) {

return +d[opts.y_column];

}));

g.select(“.axis-bottom”)
.attr(“transform”, “translate(0,” + height + “)”)
.transition()
.call(d3.axisBottom(x).ticks(10).tickSize(-height))
.select(“.domain”)
.remove();

g.select(“.axis-left”)
.transition()
.call(d3.axisLeft(y).ticks(10).tickSize(-width));

g.select(“.line”)
.datum(data)
.transition()
.attr(“d”, line);

}

var pad = function (data, size) {

var s = String(data);
while (s.length < (size || 2)) { s = “0” + s; }
return s;

}

return {

play: function (year, month) {
var d = new Date(year, month, 0),

period = year + ‘-‘ + pad(parseInt(month), 2),

criteria = “start_period=” + period + “-01&end_period=” + period + “-” + d.getDate() + “&currency=USD”;

if (dataSet[period] == undefined) {
d3.json(opts.uri + “/?” + criteria)
.header(‘api-key’, ‘U9G1L457H6DCugT7VmBaEacbHV9RX0PySO05cYaGsm’)
.get(function (error, root) {
if (!error) {
var result = root.result;
if (result.success) {

var data = result.data.data_detail;
dataSet[period] = data;
display(dataSet[period]);

}
}
})
} else {

display(dataSet[period]);

}

}
};

}

// end of file BotChartPlayer.js

4. สุดท้ายครับ เติม code ให้สมบูรณ์ไปที่ file DAILY_AVG_EXG_RATE.html เพื่อแสดงผลของเรา ตามนี้ครับ

 

ขออธิบายหน่อยนึง

กล่องสีเขียว คือ form สำหรับผู้ใช้ ใส่ input ปี, เดือน และ คำสั่ง Select เพื่อแสดง chart

กล่องสีส้ม คือ ส่วนพื้นที่เพื่อใช้แสดง chart โดยเมื่อผู้ใช้กดคำสั่ง Select แล้วแอพจะ ไปเรียก Web API เพื่อเอาข้อมูลมา สร้าง chart โดย class file BotChartPlayer.js ที่ load เข้ามาในบรรทัดที่สอง

กล่องสีน้ำเงิน คือ ขั้นตอนสร้าง BotChartPlayer ใช้สร้าง chart และ เรียก Web API กับขั้นตอน นำค่า ปี และ เดือน เพื่อส่งไปให้ Web API สำหรับช่วงเวลาข้อมูลอัตราแลกเปลี่ยน

เสร็จแล้วครับผม ทีนี้ก็ลองเล่นดูครับ โดยเปิดด้วย browser ปกติครับผม ถ้ามีเวลาผมก็จะค่อยๆเพิ่มการแสดงผล chart สำหรับ BOT Web API อื่นๆที่เหมาะสมต่อไปครับ ตาม source code ได้ที่ ChartBOT

ตัวอย่าง Bar chart ข้อมูลจาก BOT Web API อัตราดอกเบี้ยเงินให้สินเชื่อของธนาคารพาณิชย์ (อัตราร้อยละต่อปี)

ขอบคุณครับ

#:P

เก็บข้อมูล time-series ด้วย InfluxDB

สวัสดีครับ

วันนี้จะมานำเสนอการเก็บข้อมูลแบบ Time-series ด้วยฐานข้อมูลสำหรับแก้ปัญหางานนี้โดยเฉพาะที่ชื่อว่า InfluxDB

ข้อมูล Time-series เป็นข้อมูลแบบ Stream ชนิดหนึ่ง คือมันเป็นข้อมูลสถานะจากวัตถุในเวลาหนึ่ง แต่ถูกเก็บไว้อย่างต่อเนื่อง ดังนั้น เราจึงเห็นการเปลี่ยนแปลงสถานะของวัตถุนั้นในเวลาต่างๆอย่างต่อเนื่องให้เห็นรูปแบบพฤติกรรมของวัตถุนั้นได้ เพื่อตัดสินใจวางแผนแก้ปัญหาต่างๆต่อไป ตามจุดประสงค์ของปัญหาในวัตถุนั้นๆ (Domain problems)

วัตถุที่ว่านี้ เช่น ทรัพยากร, ผู้ใช้ซอฟแวร์, log ข้อมูลต่างๆของ server, Orders, Inventory, Company, Government และสิ่งต่างที่ สามารถวัดค่าบางอย่างได้ รวมเรียกว่าวัตถุ

วัตถุในเป้าหมาย จะต้องถูกวัดค่า(Measure) ได้ โดยมันจะมีคุณลักษณะ หรือ feature ที่แตกต่างกันได้ด้วย รวมเรียกว่า สถานะ จะถูกเก็บไว้นะเวลาหนึ่ง อย่างต่อเนื่อง ที่เรียกว่า Time-series นั่นเอง

ลงมือทำกันเลยครับ

  1. ติดตั้ง wget สำหรับ download Influx download ได้ที่ https://eternallybored.org/misc/wget/ ผมเลือก wget64.exe ได้ file มาแล้ว ให้ set PATH Environment เพื่อใช้งานไดสะดวก
  2. จัดเก็บข้อมูลด้วย InfluxDB

เข้าไป download ได้ที่ https://portal.influxdata.com/downloads หรือใช้ wget เข้า Command prompt แล้วพิมพ์

> wget64 https://dl.influxdata.com/influxdb/releases/influxdb-1.3.5_windows_amd64.zip

แตก zip ออกมาครับ แล้วเปิด command prompt ให้ Run as administrator แล้ว cd ไปที่ folder ที่แตก zip นี้ พิมพ์คำสั่ง เพื่อ start InfluxDB แบบนี้

> influxd -config influxdb.conf

พอ start influxdb ลองทดสอบ สร้าง, query กันดูครับ เข้า command prompt พิมพ์ แล้ว cd ไปที่ influxdb ที่เพิ่งแตก zip ออกมา พิมพ์คำสั่ง เพื่อ connect เข้าใช้งาน

> influx

ถ้าได้ จะขึ้นเหมือนเราแบบนี้

ทีนี้สร้าง db ชื่อว่า testdb ดูครับ

> CREATE DATABASE testdb

แสดง DATABASE ทั้งหมดที่มีออกมา ก็จะเห็น testdb ที่เพิ้งสร้าง

> SHOW DATABASES

เข้าใช้ database testdb แล้วเพิ่มข้อมูลลงไป โดย structure การสร้าง มีโครงสร้างแบบนี้ครับ

<measurement>[,<tag-key>=<tag-value>…] <field-key>=<field-value>[,<field2-key>=<field2-value>…] [unix-nano-timestamp]

<measurement> คือ เป้าหมายสิ่งที่เราต้องการวัดค่า หรือ ทรัพยากรที่เรามีอยู่ เช่น Orders, Claims, CPU, Client, Host เป็นต้น ใน Data warehouse เทียบได้กับ Fact Table

<tag-key>=<tag-value> คือ ชื่อตัวแปลแบบ label กับ ค่าของมัน คือ ตัวแปลที่บอกคุณลักษณะของ measurement มีชนิดข้อมูลเป็น label, nominal หรือ category เช่น Gender ={ Male, Female }, HostName = { host1, host2, … }, OrderStatus = { Pending, Rejects, Cancel, Paid } ใน Data warehouse เทียบได้กับ Dimension

*ค่า tags ต้องไม่มีช่องว่าง

<field-key>=<field-value> คือ ชื่อตัวแปลแบบวัดค่าได้ กับ ค่าของมัน คือ ตัวแปลที่บอกปริมาณของ measurement มีชนิดข้อมูลเป็นตัวเลข เช่น CPU Usage, Revenue, Expense, Income, Total Sale Amount ใน Data warehouse เทียบได้กับ Fact Value

[unix-nano-timestamp] เป็นเวลาที่บันทึก measurement นี้ได้ หน่วยเป็นตัวเลข ถ้าไม่ระบุ จะกำหมดว่าเวลานั้นเป็นสถานะของวัตถุ

ลองสร้างข้อมูลที่ testdb ก่อนอื่นไปที่ testdb ก่อน

> USE testdb

คำสั่ง สร้างข้อมูล

> INSERT cpu,host=”Server-A” total_usage=0.64

> INSERT home,owner=”#:12″,address=”123_456_Bangkok_10110″ total_cost=9291,door_lock=0,peple=0,grass_healthy=0.8,dog_food=0.5,fish_food=0.2

คำสั่ง query ข้อมูล

> SELECT host, total_usage FROM cpu

> SELECT * FROM home WHERE door_lock = 1 AND peple > 0

จะเห็นว่า คำสั่งใช้งานไม่ยาก เหมือน SQL ที่เราคุ้นเคยกัน

ลองพิพม์คำสั่งตามตัวอย่างแล้วดูผลลัพธ์นะครับ ถ้ายังไม่ได้ ก็อย่าเพิ่งไปต่อ ถ้าได้แล้วก็เปิดมันทิ้งไว้

ขั้นต่อไป เราจะเก็บข้อมูลด้วย Telegraf กัน

ตัว Telegraf จะคอยวนเก็บข้อมูล Host ให้เรา ข้อมูลที่เก็บอเช่น CPU, Memory, Hard disk และอื่นๆที่เกี่ยวกับ Host ของเรา เริ่มติดตั้ง และทดลองใช้งานกันเลยครับ

  1. เก็บข้อมูลด้วย Telegraf

เข้าไป download Telegraf ด้วย wget ที่ติดตั้งไว้แล้ว ที่ข้อ 1 จากนั้นใช้มันเพื่อ download Telegraf ครับ แบบนี้

> wget64 https://dl.influxdata.com/telegraf/releases/telegraf-1.4.0_windows_amd64.zip

พอได้มาแล้วให้แตก zip ออกมา แล้ว start มันได้เลย เข้า command prompt แล้ว cd ไปที่ folder ที่แตก zip ออกมา พิมพ์คำสั่ง เพื่อ start มันครับ

> telegraf -config telegraf.conf

ลองเปิดเข้าไปดู config file telegraf.conf ของ telegraf จะเห็นโครงสร้างข้อมูลที่มันเก็บให้เราครับ

ใน section ของ INPUTS

และ ในส่วนของ agent จะเป็น config เกี่ยวกับความถี่ในการเก็บข้อมูล ค่าเริ่มต้นจะเป็นทุก 10 วินาที

ครับทีนี้ เราของเข้าไปที่ Influx แล้ว use database telegraf  แล้ว query ดูครับ

> USE telegraf

> SELECT * FROM win_cpu LIMIT 1

ผลที่แสดงออกมาก็ดูไม่ค่อยรู้เรื่องเท่าไร ก็ไปใช้ซอฟแวร์อีกตัวเพื่อดูผลลัพธ์ แบบ time-series ที่เรียกว่า Chronograf กันต่อเลยครับ

  1. สำรวจข้อมูลด้วย Chronograf

ตัวนี้เรา สามารถ download ตรงๆได้เลยที่

https://dl.influxdata.com/chronograf/releases/chronograf-1.3.8.1_windows_amd64.zip

ได้ file มา ก็ทำเหมือนเดิมครับ แตก zip แต่สำหรับตัวนี้ start ง่ายหน่อย click ขวา ที่ chronograf.exe แล้วเลือก Run as administrator ด้วยนะครับ start Chronograf ก็เสร็จแล้ว ลองเล่นดูเข้าไปที่ http://localhost:8888/

ครั้งแรกให้ ใส่ Connection string: localhost:8086 มันคือ server InfluxDB ที่เรา start ไว้แล้วที่ข้อ 2 และ Name Influx-1 ใส่ Username / Password เสร็จแล้วกด Add source ครับ

เราจะสร้าง Dashboards แรกกัน

1. ไปที่ menu ด้านซ้าย เลือก Dashboards กด Create Dashboards

2. ที่ Cell แรก กด Add Graph ครับ

3. Add Query สำหรับ measure CPU ที่ tab ล่างสุด เลือก database telgraf > win_cpu > Percent_User_Time แล้วกด Save

4. กด Add Cell จะเพิ่ม Cell ว่างเข้ามาใน Dashboards ของเราครับ แล้วกด Add Graph

5. กด Add Query สำหรับ measure Memory ไปที่ tab ล่างสุด เลือก database telgraf > win_mem > Available_Bytes แล้วกด Save ครับ

เสร็จแล้วครับ Dashboards แรก ปรับแต่ง เปลี่ยนชื่ออีกเล็กน้อย ก็จะได้ผลลัพธ์แบบนี้

ก็ลองนำไปใช้ประโยชย์กันนะครับ สำหรับข้อมูลแบบ time-series ด้วย InfluxDB

ผมขอจบบล็อคไว้เพียงเท่านี้ หวังว่าจะเป็นประโยชน์นะครับ

ขอบคุณครับ

#:P

 

 

สร้าง Live Video ด้วย OpenCV และ Kafka

สวัสดีครับ

วันนี้เราก็มานำเสนอต่อด้วยตัวอย่างประยุกต์ใช้ OpenCV อีกหน่อย ต่อเนื่องจากบล็อคก่อนนี้ บล็อคนี้เราจะมาพัฒนาแอพถ่ายทอดสดกันครับ

หลักการก็มีอยู่ว่า พอเราใช้ OpenCV จับ frame ภาพมาแล้วเราต้องการที่จะ ส่งไปยัง แอพผู้รับที่อยู่ไกลออกไป โดยสามารถเห็นภาพที่เราถ่ายอยู่ในขณะเดียวกันนี้ พร้อมกันได้

ดังนั้น เราจะต้องมีสื่อกลางอย่างหนึ่งสำหรับสื่อสาร รับ/ส่งข้อมูล โดยต้องมีตัวส่งข้อมูลภาพ ที่เป็น frame ต่อเนื่องรัวๆนี้ หรือที่เรียกว่า stream ข้อมูล และ ต้องมีตัวคอยรับข้อมูล stream frame ภาพนั้นนำมาแสดงได้อย่างต่อเนื่องรัวๆ เพื่อจะได้เห็นเป็นภาพเคลื่อนไหวได้ ไปพร้อมๆกัน ไม่ว่าจะอยู่ที่ไหนก็ตาม ในแบบที่เรียกว่า real time

สื่อกลางตัวสื่อสารรับส่งข้อมูล stream ที่เราจะนำมาใช้ก็คือ Kafka ครับ Kafka คือ Streaming platform ที่ทำหน้าที่เป็นสื่อกลางแบบเดียวกับ Messaging system แต่มันถูกพัฒนามาเพื่อรับส่งข้อมูลแบบ Streaming โดยเฉพาะ และมันยังสามารถเก็บ stream ข้อมูลไว้ในแบบ time-series ใช้อ่านซ้ำเพื่อประโยชน์ในงานอื่นได้อีกด้วย

ก็ไปอ่านเพิ่มเติมได้ที่ https://kafka.apache.org/intro.html

ภาพนี้เป็นโครงสร้างของแอพถ่ายทอดสดนี้ครับ

จากภาพ มี live_producer เป็นแอพสำหรับ capture ภาพจากโลกจริงเป็น frame ต่อเนื่องแสดงถี่ๆ ก็จะเป็นภาพเคลื่อนไหว Kafka system เป็นระบบสื่อกลาง รับ/ส่ง frame ภาพต่อเนื่องที่จับได้จาก live_producer Kafka system จะทำงานคล้ายๆ อากาศ ที่ส่งข้อมูลคลื่นเสียงได้ และ OpenCV.Live คือ แอพสำหรับ รับภาพถ่ายทอดสดเป็น frame ต่อเนื่องจาก Kafka system OpenCV.Live แอพ สามารถเปิดรับข้อมูลภาพถ่ายทอดสดได้หลายตัว ในภาพเราแสมดไว้ 4 ตัว

เอาละ ดูภาพโครงสร้างความเชื่อมโยงของแอพต่างๆแล้ว เรามาเริ่มลงมือพัฒนามันกันเลยดีกว่าครับ

Download และ ติดตั้ง Kafka

1. เข้าไป download Kafka ได้ที่ https://kafka.apache.org/downloads เลือก Binary kafka_2.12-0.11.0.0.tgz

2. จะได้ file kafka_2.12-0.11.0.0.tgz มาแตก zip ออก (ตัวอย่างเขียนบล็อคเราวางไว้ที่ drive E: )

ก่อนจะ run Kafka เราต้อง start Zookeeper ก่อน มันทำหน้าที่เป็น tasks load balancing  ให้กับ Kafka

3. เปิด Command Prompt แล้วใช้คำสั่ง cd ไปที่ folder kafka_2.12-0.10.2.0 ที่แตก zip ออกมาแล้ว

4. พิมพ์คำสั่งข้างล่างนี้เพื่อ start Zookeeper

bin\windows\zookeeper-server-start config\zookeeper.properties

ต่อไปก็ start Kafka ทำหน้าที่เหมือน Messing system ที่สามารถรับส่งข้อมูลระหว่างแอพแบบ Streamได้

5. เปิด Command Prompt โดยเปิดแบบ Run as administrator ใช้คำสั่ง cd ไปที่ folder kafka_2.12-0.10.2.0

6. พิมพ์คำสั่งข้างล่างนี้เพื่อ start Kafka

bin\windows\kafka-server-start config\server.properties

เสร็จแล้วครับขั้นตอน download และ ติดตั้ง Kafka server

ขั้นต่อไปเราจะพัฒนาแอพ ที่ทำหน้าที่ถ่ายทอดสด โดยมันจะจับภาพจากกล้องแล้ว ส่งข้อมูลไปยัง Kafka อย่างต่อเนื่องเป็น stream data

พัฒนาแอพถ่ายทอดสด

Code ส่วนนี้เขียนด้วย Python ใช้ library OpenCV วิธีติดตั้ง และใช้งานดู้ได้จากบล็อคของเราที่ได้นำเสนอไปแล้วเรื่อง สร้างดวงตาให้ AI ด้วย OpenCV เขียนด้วย Python บน Windows

ส่วนที่ทำหน้าที่ส่งข้อมูลภาพจากกล้อง ไปยัง Kafka จะใช้ library อีกตัวที่ชือว่า Kafka Python client ติดตั้งกันก่อนครับ

1. ติดตั้ง Kafka Python client ด้วย pip เปิด Command Prompt แล้วพิมพ์คำสั่งนี้ลงไป

pip install kafka-python

อ่านข้อมูลเพิ่มเติมได้ที่ https://github.com/dpkp/kafka-python

2. สร้าง file live_producer.py เพื่อเขียน code Python แล้วเขียน code ตามข้างล่างนี้ลงไปครับ

import cv2

from kafka import KafkaProducer

producer = KafkaProducer(bootstrap_servers=’localhost:9092′)

cap = cv2.VideoCapture(0)

while(True):

    ret, frame = cap.read()

    cv2.imshow(‘live_camera’, frame)

    retval, img  = cv2.imencode(‘.png’, frame)

    producer.send(‘live-no99’, img.tobytes())

    if cv2.waitKey(1) & 0xFF == ord(‘q’): break

cap.release()

cv2.destroyAllWindows()

ผมขออธิบาย code เฉพาะส่วนที่ติดต่อกับ Kafka

producer = KafkaProducer(bootstrap_servers=’localhost:9092′) คือ สร้าง class KafkaProducer กำหนด bootstrap_servers ไปยัง URL ของ Kafka server ก็คือ localhost:9092 ที่เราได้เปิดไว้แล้ว เราสามารถเพิ่ม kafka หลายๆเครื่องเป็นกลุ่ม เพื่อประโยชน์ในการกระจายงานได้ โดยขั้นด้วย , ต่อๆกันไป เช่น  kafka-1-ip:9092, kafk-2-ip:9092,…. kafka-n-ip:9092

ดู code ส่วนของ while loop หลังจากจับ frame ภาพได้แล้วแสดงด้วย code frame = cap.read() ก็จะทำการ encode เป็น file ภาพ .png ก่อน ด้วย code retval, img = cv2.imencode(‘.png’, frame) แล้วเรียกใช้ function send ของ producer เพื่อส่งภาพ ไปยัง channel live-99 ต่อไปแสดงด้วย code producer.send(‘live-no99’, img.tobytes())

เสร็จแล้วครับ แอพในส่วนของถ่ายทอดสด ลอง run ขึ้นมาลยครับ เปิด Command Prompt แล้ว run code live_producer.py ด้วยคำสั่ง

python live_producer

พอ run code แล้ว แอพก็จะมีหน้าตาคล้ายๆภาพข้างล่างแบบนี้ ให้เปิดทิ้งไว้เลยครับ ตัวอย่าง มันก็จะมืดๆหน่อยนะครับ

Code ที่เสร็จสมบูรณ์ อยู่ที่ https://github.com/chavp/openCV_with_Python/blob/master/live_producer.py

เรามีแอพที่ทำหน้าที่ส่งข้อมูลภาพ เป็นแบบ stream สดๆ หรือ real time แล้ว ทีนี้ก็ต้องมีแอพ ที่เป็นตัวรับข้อมูล stream ภาพมาแสดงด้วยอีกตัวหนึ่ง

สำหรับแอพตัวนี้ผมจะ ใช้ C# .NET พัฒนา เป็นแบบวินแอพ WPF ทำหน้าที่แสดงผลภาพถ่ายทอดสด ด้วย class System.Windows.Controllers.Image ของ WPF แล้วจะใช้ library ที่เรียกว่า Confluent.Kafka เพื่อคอยรับ

streaming data ภาพจาก channel live-99  บน Kafka มาส่งให้ class System.Windows.Controllers.Image เพื่อแสดงผลภาพต่อเนื่องอีกที

1. เปิด Visual Studio 2017 Community ครับ (ถ้ายังไม่มี ก็ไป download และติดตั้งได้ที่ https://www.visualstudio.com/vs/whatsnew/)

2. สร้าง projects WPF ตั้งชื่อ OpenCV.Live ดูภาพ

ต่อไปเราจะติดตั้ง package Confluent.Kafka

3. ไปที่ menu Tools > NuGet Package Manager > Package Manager Console ขึ้นมาจะอยู่ที่ tab ด้านล่าง พิมพ์คำสั่ง

Install-Package Confluent.Kafka

4. สร้างหน้าจอ windows สำหรับแสดงผล แก้ไข file MainWindow.xaml แบบข้างล่างนี้

<Window x:Class=”OpenCV.Live.MainWindow”

        xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”

        xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”

        xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″

        xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″

        xmlns:local=”clr-namespace:OpenCV.Live”

        mc:Ignorable=”d”

        Title=”Channal-99″ Height=”350″ Width=”525″>

    <Grid>

        <Image Name=”videoLiveView” Margin=”10″/>

    </Grid>

</Window>

5. เพิ่ม class ImageStreamDeserializer.cs เพื่อแปลงข้อมูล bytes ที่รับจาก channel live-99  ไปเป็นข้อมูลชนิดอื่นตามที่เราต้องการ ในที่นี้รับมา ไม่ต้องทำอะไร ส่งกลับออกไปเลย

namespace OpenCV.Live

{

    using Confluent.Kafka.Serialization;

    public class ImageStreamDeserializer : IDeserializer<byte[]>

    {

        public byte[] Deserialize(byte[] data)

        {

            return data;

        }

    }

}

7. ไปที่ file MainWindow.xaml.cs แล้วเพิ่ม code ที่คอยข้อมูลจาก channel live-99  ที่ live_producer.py คอยจับภาพจากกล้องแล้วส่งมาอย่างต่อเนื่องแบบเป็น bytes ข้อมูล เข้ามา ซึ่งตอนนี้ก็น่าจะเปิดทิ้งไว้อยู่

using System;

using System.IO;

using System.Collections.Generic;

using System.Threading.Tasks;

using System.Windows;

using System.Windows.Media.Imaging;

namespace OpenCV.Live

{

    using Confluent.Kafka;

    /// <summary>

    /// Interaction logic for MainWindow.xaml

    /// </summary>

    public partial class MainWindow : Window

    {

        public MainWindow()

        {

            InitializeComponent();

            var config = new Dictionary<string, object>

            {

                { “group.id”, “live-no99-group” },

                { “bootstrap.servers”, “localhost:9092” }

            };

            Task.Factory.StartNew(() =>

            {

                using (var consumer = new Consumer<Null, byte[]>(config, null, new ImageStreamDeserializer()))

                {

                    consumer.Assign(new List<TopicPartitionOffset> { new TopicPartitionOffset(“live-no99”, 0, Offset.End) });

                    while (true)

                    {

                        Message<Null, byte[]> msg;

                        if (consumer.Consume(out msg, TimeSpan.FromSeconds(1)))

                        {

                            using (var stream = new MemoryStream(msg.Value))

                            {

                                this.Dispatcher.Invoke(() =>

                                {

                                    videoLiveView.Source = BitmapFrame.Create(stream,

                                                                      BitmapCreateOptions.None,

                                                                      BitmapCacheOption.OnLoad);

                                });

                            }

                        }

                    }

                }

            });

        }

    }

}

8. เสร็จแล้วครับ ซอฟแวร์สำหรับแสดงข้อมูลภาพแบบถ่ายทอดสด เราได้ทดลอง run ออกมาหลายๆ หน้าจอ ผลก็แสดงได้ดังข้างล่างนี้

code ที่สมบูรณ์ https://github.com/chavp/openCV_with_Python/tree/master/OpenCV.Live

ง่าย และยืดหยุ่นมากเลยใช่มั้ยละครับ การถ่ายถอดสด video ด้วย OpenCV และ Kafka ก็หวังว่าบล็อคนี้จะมีประโยชน์ ในการนำไปประยุกต์ใช้พัฒนาแอพต่างๆต่อไป เราก็ขอจบบล็อคไว้เพียงเท่านี้ครับ

ขอบคุณครับ

#:P

 

#c, #kafka, #live-video, #opencv, #python

สร้างดวงตาให้ AI ด้วย OpenCV เขียนด้วย Python บน Windows

สวัสดีครับ

วันนี้ก็มานำเสนองานที่เกี่ยวข้องกับ AI ส่วนหนึ่งก็คือ การมองเห็น สำหรับ AI เป็นก็คือ Computer ชนิดหนึ่ง ตาของมันก็ต้องเป็นกล้อง Camera

บล็อคนี้ผมก็จะแนะนำให้รู้จัก OpenCV มันคือ Library open source สำหรับจับ, ประมวลผล และ แสดงผลภาพ จากกล้อง Camera ในคอมพิวเตอร์ หรือ มือถือของเราครับ

เริ่มทำกันเลยครับ

ก่อนอื่น เราต้องมีกล้อง Camera ก่อนครับ ของผมติดอยู่บนเครื่อง notebook แล้ว สมัยนี้น่าจะมีกันทุกคน

งานนี้ผมจะเขียนด้วย Python ครับ ผมใช้ WinPython เข้าไป download  และ ติดตั้งได้ที่ https://winpython.github.io/ ผมเลือก 64bit

ต่อไปก็ ต้องลง 2 module คือ numpy และ openCV โดยใช้ wheel ครับ ต้อง download 2 file นี้มาก่อนครับ

  1. numpy-1.13.1+mkl-cp36-cp36m-win_amd64.whl
  2. opencv_python-3.3.0-cp36-cp36m-win_amd64.whl

เข้าไป download ได้ที่ http://www.lfd.uci.edu/~gohlke/pythonlibs/

พอได้มาแล้ว copy ทั้ง 2 files ไปวางไว้ที่ folder C:\Users\windows แล้วเข้า Command Prompt พิมพ์คำสั่ง ข้างล่างนี้ลงไปครับ

ติดตั้ง numpy

pip install numpy-1.13.1+mkl-cp36-cp36m-win_amd64.whl

เสร็จแล้ว ติดตั้ง opneCV ต่อ

pip install opencv_python-3.3.0-cp36-cp36m-win_amd64.whl

ติดตั้งเสร็จแล้วก็ เริ่มเขียน code ได้เลย

โปรเจ็คแรกจะทดสอบแสดงภาพ Camera ด้วย openCV กันก่อน

1. สร้าง file first_camera.py แล้วเขียน code ตามข้างล่างนี้ลงไป

2. import numpy และ cv2 เข้ามา

import numpy as np

import cv2

3. จับภาพจากกล้องหน้า

cap = cv2.VideoCapture(0)

4. วนดำเนินการนำภาพ มาแสดงที่ windows เรื่อยๆ ก็คือการแสดงภาพเคลื่อนไหวนั่นเองครับ โดยผมจะ process frame ก่อน เพื่อแสดงเป็นภาพขาวดำครับ

while(True):

    # จับภาพแบบ frame by frame

    ret, frame = cap.read()

    # ตรงนี้ผมจะแสดงภาพออกมาเป็นขาวดำ โดยคำนวนผ่าน function cvtColor

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # แสดงผล frame ที่เป็นขาวดำครับ

    cv2.imshow(‘frame’, gray)

    if cv2.waitKey(1) & 0xFF == ord(‘q’): # หน้าจอ จะปิดก็ต่อเมื่อ ผมกด q

        break

5. จบวน while แล้วก็ให้ ปล่อย Camera และทุก windows ที่แสดงภาพ  ครับ

cap.release()

cv2.destroyAllWindows()

ภาพตัวอย่าง video ที่แสดงผ่าน openCV โดยแต่งสีภาพเป็นสีเทาก่อนครับ

Code สมบูณร์แล้ว ดูได้ที่ https://github.com/chavp/openCV_with_Python/blob/master/first_camera.py

ลำดับต่อไป ผมจะติดตามวัตถุในภาพ โดยการจับวัตถุบนภาพ ซึ่งเป็นงานประยุกต์ที่เราเห็นทั่ว ไป พอติดตามวัตถุได้ ก็นำไปค้นหา โดยใช้เทคนิควิเคราะห์ข้อมูล Machine learning  ซึ่งผมจะนำเสนอต่อไป เพื่อระบุว่าเป็นวัตถุอะไร มีคุณสมบัติอะไร แล้วก็สามารถทำสิ่งต่างๆ เช่น ส่งผลลัพธ์ให้ AI เพื่อเล่นเกม หรือทำงานต่างๆ ต่อไปนั่นเอง

เทคนิคการติดตามวัตถุ มีหลายเทคนิค สำหรับ บล็อคนี้ ผมจะนำเสนอ การติดตามใบหน้าคนครับ

การติดตามใบหน้า ก็ต้องมีการตรวจจับใบหน้าให้ได้ก่อน โดยใช้เทคนิคการตรวจจับใบหน้าที่เรียกว่า Haar Cascade Classifier โดย feature ที่ใช้ตรวจจับใบหน้า จะอยู่ในรูป format xml ซึ่งมีคน train ไว้ให้แล้ว ผมจะนำมาโหลดเข้าไปใน Haar cascade classifier เพื่อตรวจจับใบหน้าได้เลย อ่านเพิ่มเติมได้ที่ http://www.cs.ubc.ca/~lowe/425/slides/13-ViolaJones.pdf

เข้าไป  download file Haar features มาก่อนครับที่ https://github.com/opencv/opencv/tree/master/data/haarcascades

ผมเลือก download เฉพาะ file haarcascade_frontalface_default.xml เพื่อตรวจจับใบหน้าคนครับ ในนั้นก็มี feature ตรวจจับลูกตา, หน้ายิ้ม และอื่นๆด้วย

ต่อไปก็เขียน code สำหรับติดตามหน้าคนได้แล้วครับ

1. สร้าง file face_detector.py

2. copy code จาก file first_camera.py มาใส่ที่ file นี้ได้เลยครับ

3. สร้าง face cascade โดยโหลด file haarcascade_frontalcatface.xml ที่ download มาครับ

import numpy as np

import cv2

face_cascade = cv2.CascadeClassifier(‘haarcascade_frontalcatface.xml’)

ที่ใส่สีดำไว้ เพื่อให้เห็นความต่างของ code ที่เพิ่มเติมเข้าไปจาก first_camera.py

4. ในวน loop while หลังจากที่สร้าง gray frame แล้ว ให้ใช้ face_cascade เพื่อตรวจจับใบหน้าคนครับ

gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

face_rects = face_cascade.detectMultiScale(gray, 1.3, 5)

5. จาก code ด้านบนเราจะได้รายการของ frame สี่เหลี่ยมที่จับได้ว่าเป็นใบหน้าคนมา เราก็จะนำ face_rects วาดสี่เหลี่ยมทับไปบน frame ก่อนนำไปแสดงผลครับ

for (x,y,w,h) in face_rects:

                            cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0), 3)

        # แสดงผล frame ที่ถูกครอบด้วยสี่เหลี่ยมบนใบหน้าแล้ว

                           cv2.imshow(‘face_detector’, frame)

ภาพตัวอย่าง video ที่แสดงผ่าน openCV โดยเพิ่มการติดตามภาพหน้าคนเข้าไปครับ

Code สมบูณร์แล้ว ดูที่ https://github.com/chavp/openCV_with_Python/blob/master/face_detector.py

เสร็จแล้วครับ การติดตามหน้าคนบน Camera จะเห็นว่า cv2 มันคอยจับภาพ(capture) ออกมาจาก Camera เป็น image แล้ว เราก็ใช้ Image processing เทคนิคที่ชื่อว่า Haar cascade เพื่อจับตำแหน่งหน้าคนบนภาพ ต่อจากนั้นเราก็นำต่ำแหน่งที่ได้มา แก้ไขภาพแล้วแสดงออกอีกที ในทางอื่น เราก็สามารถส่งภาพที่จับได้นี้ไปวิเคราะห์ใน module  หรือ application อื่นได้อีก เช่น Argument reality, Image processing หรือ Service อื่นๆ เพื่อวิเคราะห์เจาะจงลงไปได้อีก หลักการมีเท่านี้ ก็ทำให้ AI ของเรามีดวงตา และฉลาดได้อีกครับ

ขอจบบล็อคไว้เท่านี้ หวังว่าจะมีประโยชน์

ขอบคุณครับ

#:P

ศึกษาเพิ่มเติมได้ที่: http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_tutorials.html

#opencv, #python

เรียนรู้ AI ใช้เล่น Game ในแบบง่ายๆก่อน

สวัสดีครับ

วันหยุดนี้ ผมก็ว่างมาเขียนบล็อค AI เพื่อเล่น Game ด้วย Python ง่ายๆ โดยเริ่มจาก Game ผู้เล่น 2 คนครับ ซึ่งผมคิดว่า วิธีที่ง่ายที่สุด ในการเรียนรู้ ทำความเข้าใจ AI ก็คือ เอามันมาเล่น Game กับเรา หรือ สร้างมันให้เล่น Game กับคนอื่นแทนเราได้

บล็อคนี้ก็จะเป็น บล็อคเริ่มต้นของผมเอง ในการจะนำตัวเองไปพัฒนา AI สำหรับเล่น Game หรือ คิด วิเคราะห์ เพื่อทำงานอื่นๆ แทนผมเอง ซึ่งอาจจะมีความซับซ้อนมากขึ้นเรื่อยๆ Library แรกที่ผมจะนำเสนอนนี้เรียกว่า easyAI

easyAI เป็น game AI เฉพาะ ที่พัฒนาจาก Python สำหรับเกมส์ ผู้เล่น 2 คน เช่น Tic-Tac-Toe, Connect 4, Hexapawn และ อื่นๆ

easyAI ใช้สร้างกลไกของเกมส์ เพื่อให้เราเล่น กับ AI หรือ ให้ AI เล่นกันเอง เล่นกับคนอื่น แะสามารถใช้แก้ปัญหาของเกมส์เพื่อหาทางเลือกที่ดีที่สุดของเกมส์ ใช้เป็นผลฉลยสำหรับ Game ต่อไปได้

ลงมือทำด้วยตัวอย่างง่ายๆ GameOfBones

กฎการเล่นก็คือ มีผู้เล่น 2 คน, มีกองกระดูกอยู่ 20 ชิ้น, ผู้เล่นจะต้องสลับกันหยิบ โดยในตาของผู้เล่น ผู้เล่นสามารถหยิบได้ 1, 2 และ 3 ชิ้น เกมจะจบลงเมื่อกระดูกหมด และ ผู้เล่นที่หยิบกระดูกชิ้นสุดท้าย จะเป็นผู้แพ้

1. เริ่มต้นคุณต้องติดตั้ง Python ในเครื่องก่อน ผมใช้ Windows 10 จึงติดตั้ง WinPython download และ ติดตั้งได้ที่ https://winpython.github.io/

ติดตั้งเสร็จทดสอบ Python เข้า Command Prompt  (Serach windows พิมพ์ cmd) พิมคำสั่ง

py –version

2. ติดตั้ง easyAI พิมคำสั่ง

pip install easyAI

3. สร้าง file game_of_bones.py แล้วเขียน code ตามข้างล่างนี้ โดย import mdule easyAI เข้ามาก่อน แล้วเขียน class Game Rule ต่อไป

from easyAI import TwoPlayerGame, Human_Player, AI_Player, Negamax

class GameOfBones(TwoPlayerGame):

              ”’ ผู้เล่น สามารถเอากระดูกออกได้ 1, 2, 3 ชิ้น จากกอง ผู้เล่นที่เอากระดูกออกชิ้นสุดท้าย จะเป็นผู้แพ้ ”’

               def __init__(self, players):

                              self.players = players

                              self.pile = 20 # เริ่มต้นมีกระดูกในกอง 20 ชิ้น

                           self.nplayer = 1 # ผู้เล่นคนแรก เริ่มเล่นก่อน

             def possible_moves(self): return [ ‘1’, ‘2’, ‘3’ ] # ทางเลือกที่ผู้เล่นสามารถเลือกได้ทั้งหมด

               def make_move(self, move): self.pile -= int(move) # เมื่อผู้เล่นเลือกเล่นแล้ว สถานะเกมจะเปลี่ยน

             def win(self): return self.pile <= 0 # ผู้เล่นที่อยู่ในเงื่อนไขนี้ จะเป็นผู้ชนะ หรือในทางตรงข้ามคือแพ้

             def is_over(self): return self.win() # เกมส์จะจบลงเมื่อมี ผู้เล่นชนะ

             def show(self): print(self.pile, “ bones left in the pile”) # แสดงสถานะของ game ล่าสุด

             def scoring(self): return 100 if game.win() else 0 # ผู้ชนะได้ 100 นอกนั้นได้ 0 สำหรับ AI

ai = Negamax(13) # AI จะคิดตาเล่น 13 ครั้งล่วงหน้า เป็นความยากของ AI ลองปรับค่าดู

game = GameOfBones( [Human_Player(), AI_Player(ai)] )

history = game.play()

จาก code จะเห็นว่ากำหนด AI algorithm เป็น Negamax(13) ใน GameOfBones นั่นก็คือ AI จะสร้าง table ผลลัพย์ที่เป็นไปได้ล่วงหน้า 13 ทางเลือก แล้วเลือกทางที่ดีที่สุด ลองปรับค่า แล้วเล่นดูจะเห็นความฉลาดเลือกเดินของ AI

4. เริ่มเล่น game ได้ครับ โดยเข้า Command Prompt แล้วพิมพ์ คำสั่ง run script แบบนี้

python  game_of_bones.py

ผลลัพธ์ที่แสดงออกมา

5. ต่อไปจะเป็นการแก้ปัญหาเกมส์ เพื่อหาตารางทางเลือกที่ดีที่สุดของเกมส์ โดยเพิ่ม method id_solve เข้ามาก่อน แล้ว run id_solve เขียน code แบบนี้ครับ

from easyAI import id_solve

r, d, m = id_solve(GameOfBones, ai_depths=range(2,20), win_score=100)

ตัวแปลมีความหมาย ดังนี้

r มีค่าเท่ากับ 1 ผู้เล่นคนแรกจะชนะเสมอ, ถ้าเท่ากับ -1 ก็คือแพ้เสมอ

d คือ จำนวนการเดินรวมกันมากที่สุด หรือ น้อยกว่านั้น จนเกมส์จบ

m คือ ทางเลือกที่ผู้เล่นคนแรกควรจะเลือกเล่นในตาแรก จึงจะได้ผลลัพย์ออกมาตามค่าที่ id_solve ออกมา

จากผลลัพย์ ของ GameOfBones จะได้ ค่า r, d, m แบบนี้

จากผลเฉลยของเกมส์ จะได้ตารางผลลัพย์ของทางเลือกทั้งหมด เพื่อใช้ตัดสินใจเลือกทางเดินที่ดีที่สุดของเกมส์ เรียกว่า Transposition table (tt) ซึ่งเราจะนำไปใส่ให้ AI ใช้ต่อไป แบบนี้

tt = TT()

GameOfBones.ttentry = lambda game: game.pile # key สำหรับ ทางเลือกเล่นใน table

r, d, m = id_solve(GameOfBones, range(2, 20), win_score=100, tt=tt)

พอเรา run id_solve แล้วเราจะได้ table ทางเลือกที่ดีที่สุด โดยใช้ key ก็คือ pile หรือ กระดูกที่เหลือค้นใน tt แล้วจะได้ทางเลือกที่ดีที่สุดออกมา เมื่อนำไปใส่ AI แล้ว ให้มันเป็นผู้เล่นคนแรก ด้วยมันจึงไม่มีทางแพ้เลย

ลอง save file transposition table ออกมาดู ด้วยคำสั่งนี้

tt.to_json_file(‘game_of_bones.jason’)

พอเปิดดูจะเห็น โครงสร้างแบบนี้ (แนะนำให้เอาไป process json format ที่ https://jsonformatter.curiousconcept.com/ จะทำให้อ่านง่ายกว่า)

สำหรับเรียนรู้และพัฒนาต่อๆไป ก็ไปอ่านได้ที่นี่เลยครับ https://github.com/Zulko/easyAI

ก็ขอจบบล็อคไว้เท่านี้ หวังว่าจะมีประโยชน์ให้ผู้เริ่มต้นสนใจพัฒนา AI ต่อไปเพื่อใช้ประโยชน์ทำงานต่อไปครับ

ขอบคุณครับ

#:P

Microservices และ Agile manifesto

​ถ้าทีมงานพัฒนาซอฟแวร์ที่มีสถาปัตยกรรมแบบ Microservices แล้วก็หมายความว่า ทีมนั้นได้เดินตามหลักการทั้ง 12 และมีค่านิยมทั้ง 4 แห่งเอจไจล์ ไปโดยอัตโนมัติเป็นที่เรียบร้อยแล้ว… ไม่ต้องแนะนำอะไรเพิ่มเติมอีกต่อไป

  • #:P –