The moons dataset and decision surface graphics in a Jupyter environment – II – contourplots

I proceed with my present article series on the “moons dataset” as an example for classification tasks in the field of “machine learning” [ML]. My objective is to gather basic knowledge on Python related tools for performing related experiments. In my last blog article

The moons dataset and decision surface graphics in an Jupyter environment – I

In the case of the “moons dataset” we can apply and train support vector machines [SVM] algorithms for solving the classification task: The trained algorithm will predict to which of the 2 clusters a new data point probably belongs. The basic task for this kind of information reduction is to find a (curved) decision surface between the data clusters in the n-dimensional representation space of the data points during the training of the algorithm.

As the moons feature space is only 2-dimensional the decision surface would be a curved line. Of course, we would like to add this line to the 2D-plot of the moons clusters shown in the last article.

The challenge of plotting data points and decision surfaces for our moon clusters

  1. is sufficiently simple for a Python- and AI/ML-beginner as me,
  2. is a good opportunity to learn how to work with a Jupyter notebook,
  3. gives us a reason to become acquainted with some basic plotting functions of matplotlib,
  4. an access to some general functions of SciKit – and some specific ones for SVM-problems.

Much to learn from one little example. Points 2 and 3 are the objectives of this article.

Contour plots !

But what kind of plots should we be interested in? We need to separate areas of a 2-dimensional parameter space (x1,x2) for which we get different (integer) target or y-values, i.e. to distinguish between a set of distinct classes to which data points may belong – in our case either to a class “0” of the first moon like cluster and a class “1” for data points around the second cluster.

In applied mathematics there is a very similar problem: For a given function z(x1,x2) we want to visualize regions in the (x1,x2)-plane for which the z-values cover a range between 2 selected distinct z-values, so called contour areas. Such contour areas are separated by contour lines. Think of height lines in a map of a mountain region. So, there is an close relation between a contour line and a decision surface – at least in a two dimensional setup. We need contour plots!

Let us see how we start a Jupyter environment and how we produce nice 2D- and even 3D-contour-plots.

Starting a Jupyter notebook from a virtual Python environment on our Linux machine

I discussed the setup of a virtual Python environment (“virtualenv”) already in the article Eclipse, PyDev, virtualenv and graphical output of matplotlib on KDE – I of this blog. I refer to the example and the related paths there. The “virtualenv” has a name of “ml1” and is located at “/projekte/GIT/ai/ml1”.

In the named article I had also shown how to install the Jupyter package with the help of “pip3” within this environment. You can verify the Jupyter installation by having a look into the directory “/projekte/GIT/ai/ml1/bin” – you should see some files “ipython3” and “jupyter” there. I had also prepared a directory
“/projekte/GIT/ai/ml1/mynotebooks”
to save some experimental notebooks there.

How do we start a Jupyter notebook? This is simple – we just use a
terminal window and enter:

myself@mytux:/projekte/GIT/ai/ml1> source bin/activate 
(ml1) myself@mytux:/projekte/GIT/ai/ml1> jupyter notebook 
[I 16:16:27.734 NotebookApp] Writing notebook server cookie secret to /run/user/1004/jupyter/notebook_cookie_secret
[I 16:16:29.040 NotebookApp] Serving notebooks from local directory: /projekte/GIT/ai/ml1
[I 16:16:29.040 NotebookApp] The Jupyter Notebook is running at:
[I 16:16:29.040 NotebookApp] http://localhost:8888/?token=942e6f5e75b0d014659aea047b1811d1992ca77e4d8cc714
[I 16:16:29.040 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 16:16:29.054 NotebookApp] 
    
    To access the notebook, open this file in a browser:
        file:///run/user/1004/jupyter/nbserver-19809-open.html
    Or copy and paste one of these URLs:
        http://localhost:8888/?token=942e6f5e75b0d014659aea047b1811d1992ca77e4d8cc714

We see that a local http-server is started and that a http-request is issued. In the background on my KDE desktop a new tag in my standard browser “Firefox” is opened for this request:

Note that a standard port 8888 is used; this port should not be used by other services on your machine.

On the displayed web page we can move to the “mynotebooks” directory. We open a new notebook there by clicking on the “New”-button on the right side of the browser window:

We choose Python3 as the relevant interpreter and get a new browser window:

We give the notebook a title by clicking on “File >> Save as …” before start using the provided input “cell” for coding

I name it “moons1” in the next input form and check afterward in a terminal that the file “/projekte/GIT/ai/ml1mynotebooks/moons1.ipynb” really has been created; you see this also in the address bar of the browser – see below.

Lets do some plotting within a notebook

Most of the icons regarding the notebook screen are self explanatory. The interesting and pretty nice thing about a Jupyter notebook is that the multiple lines of Python code can be filled into cells. All lines can be executed in a row by first choosing a cell via clicking on a it and then clicking on the “Run” button.

As a first exercise I want to do some plotting with “matplotlib” (which I also installed together with the numpy package in a previous article). We start by importing the required modules:

A new cell for input opens automatically (it is clever to separate cells for imports and for real code). Let us produce a most simple plot there:

No effort in comparison to what we had to do to prepare an Eclipse environment for plotting (see Eclipse, PyDev, virtualenv and graphical output of matplotlib on KDE – II). Calling plot routines simply works – no special configuration is required. Jupyter and the browser do all the work for us. We save our present 2 cells by clicking on the “Save“-icon.

How do we plot contour lines or contour areas?

Later on we need to plot a separation line in a 2-dimensional parameter space between 2 clustered sets of data. This task is very similar to plotting a contour line. As this is a common task in math we expect matplotlib to provide some functionality for us. Our ultimate goal is to wrap this plotting functionality into a function or class which also accepts an SVM based ML-method of SciKit to prepare and evaluate the basic data first. But let us proceed step by step.

Some research on the Internet shows: The keys to contour plotting with matplotlib are the functions “contour()” and “contourf()” (matplotlib.pyplot.contourf):

contour(f)([X, Y,] Z, [levels], **kwargs)

“contour()” plots lines, only, whilst “contourf()” fills the area between the lines with some color.

Both functions accept data sets in the form of X,Y-coordinates and Z-values (e.g. defined by some function Z=f(X,Y)) at the respective points.

X and Y can be provided as 1-dim arrays; Z-values, however, must be given by a 2-dim array, such that len(X) == M is the number of columns in Z and len(Y) == N is the number of rows in Z. We cover the X,Y-plane with Z-values from bottom to top (Y, lines) and from the left to the right (X, columns).

Somewhat counter-intuitively, X and Y can also be provided as 2-dim arrays – with the same dimensionality as Z.
There is a nice function “meshgrid” (of packet numpy) which allows for the creation of e.g. a mesh of two 2-dimensional X- and separately Y-matrices. See for further information (numpy.meshgrid). Both arrays then have a (N,M)-layout (shape); as the degree of information of one coordinate is basically 1-dimensional, we do expect repeated values of either coordinate in the X-/Y-meshgrid-matrices.

The function “shape” gives us an output in the form of (N lines, M columns) for a 2-dim array. Lets apply all this and create a rectangle shaped (X,Y)-plane:

The basic numpy-function “arange()” turns a range between two limiting values into an array of equally spaced values. We see that meshgrid() actually produces two 2-dim arrays of the same “shape”.

For test purposes let us use a function

Z1=-0.5* (X)**2 + 4*(Y)**2.

For this function we expect elliptical contours with the longer axis in X-direction. The “contourf()”-documentation shows that we can use the parameters “levels“, “cmap” and “alpha” to set the number of contour levels (= number of contour lines -1), a so called colormap, and the opacity of the area
coloring, respectively.

You find predefined colormaps and their names at this address: matplotlib colormaps. If you add an “_r” to the colormap-name you just reverse the color sequence.

We combine all ingredients now to create a 2D-plot (with the “plasma” colormap):

Our first reasonable contour-plot within a Jupyter notebook! We got the expected elliptic curves! Time for a coffee ….

Changing the plot size

A question that may come to your mind at this stage is: How can we change the size of the plot?

Well, this can be achieved by defining some basic parameters for plotting. You need to do this in advance of any of your specific plots. One also wants to add some labels for all axis. We, therefore, extend the code in our cell a bit by the following statements and click again on “Run”:

You see that “fig_size = plt.rcParams[“figure.figsize”]” provides you with some kind of array- or object like information on the size of plots. You can change this by assigning new values to this object. “figure” is an instance of a container class for all plot elements. “plt.xlabel” and “plt.ylabel” offer a simple option to add some text to an axis of the plot.

What about a 3D-representation …

As we are here – isn’t our function for Z1 not a good example to get a 3D-representation of our data? As 3D-plots are helpful in other contexts of ML, lets have a quick side look at this. You find some useful information at the following addresses:
PythonDataScienceHandbook and mplot3d-tutorial

I used the given information in form of the following code:

You see that we can refer to a special 3D-plot-object as the output of plt.axes(projection=’3d’). The properties of such an object can be manipulated by a variety of methods. You also see that I manipulated the number of ticks on the z-axis to 5 by using a function “set_major_locator(plt.MaxNLocator(5)“. I leave it to the reader to dive deeper into manipulation options for a plot axis.

Addendum – 07.07.2019: Adding a colorbar

A reader asked me to show how one can set ticks and add a color-bar to the plots. I give an example code below:

The result is:

For the 3D-plots we get:

Conclusion

Enough for today. We have seen that it is relatively simple to create nice contour and even 3D-plots in a Jupyter notebook environment. This new knowledge provides us with a good basis for a further approach to our objective of plotting a decision surface for the moons dataset. In the next article

The moons dataset and decision surface graphics in a Jupyter environment – III – scatter-plots and LinearSVC

we first import the moons data set into our Jupyter notebook. Then we shall create a so called “scatter plot” for all data points. Furthermore we shall train a specific SVM algorithm (LinearSVC) on the dataset.

Links

https://codeyarns.com/2014/10/27/how-to-change-size-of-matplotlib-plot/
matplotlib.pyplot.contourf
https://stackoverflow.com/questions/12608788/changing-the-tick-frequency-on-x-or-y-axis-in-matplotlib

 

The moons dataset and decision surface graphics in a Jupyter environment – I

For me as a beginner in Machine Learning [ML] and Python the first interesting lesson was that artificial neural networks are NOT needed to solve various types of problems. A second lesson was that I needed a lightweight tool for quick experiments in addition to Eclipse PyDev – which lead me to Jupyter notebooks. A third lessons was that some fundamental knowledge about plotting functionalities provided by Sklearn is mandatory – especially in the field of classification tasks, where you need to visualize decision or separation surfaces. For a newbie both in ML, Python and SciKit/Sklearn the initial hurdles are many and time consuming. Not all text books were really helpful – as the authors often avoid a discussion of basic code elements on a beginner level.

Playing around with a special test and training example for beginners – namely the moons data set – helped me to overcome initial obstacles and to close some knowledge gaps. With this article series I want to share some of my insights with other people starting to dive into the field of ML. ML-professionals will not learn anything new.

Classification, SVM and the moons dataset

An interesting class of ML-problems is the reduction of information in the following sense:

A set of data vectors describes data points in a n-dimensional representation space. Each individual data point in the set belongs to a specific cluster of multiple existing disjunctive clusters of data points in the representation space. After a training an algorithm shall predict for any new given data point vector to which cluster the data point belongs.

This poses a typical classification task in ML. I talk of a representation space as it represents the “n” different features of our data points. The representation space is sometimes also called “feature space”.

“Disjunctive” is to be understood in a geometrical sense: The regions the clusters occupy in the representation space should be clearly separable. A “separation surface” – mathematically a hyperplane – should exist which separates two clusters from each other.

Each cluster may exhibit special properties of its members. Think e.g. of diseases; a certain combination of measured parameter or “feature” values describing the status of a patient’s body (temperature, blood properties, head ache, reduced lung capacity, ..) may in a complicated way decide between different kinds of virus infections.

In such a case an ML-algorithm – after a training – has to separate the multidimensional representation space into disjunctive regions and afterward determine (by some exact criteria) into which region the “feature” values in some given new data vector place the related data point. To achieve this kind of prediction capability may in real world problems be quite challenging:

The data points of each known cluster may occupy a region with a complex curved or even wiggled surface in the representation space. Therefore, most often the separation surface of data point clusters can NOT be described in a simple linear way; the separation hyperplane between clusters in a multidimensional representation space is typically given by a non-linear function; it describes a complex surface with a dimensionality of n-1 inside the original n-dim vector space for data representation.

One approach to find such hyperplanes was/is e.g. the use of so called “Support Vector Machines” (as described e.g. in chapter 5 of the brilliant book of “Machine Learning with SciKit-Learn & TensorFlow” by A. Geron; published by O’Reilly, 2018).

An interesting simple example for using the SVM approach is the “moon” dataset. It displays 2 disjunctive clusters of data in a 2-dimensional
representation space ( with coordinates x1 and x2 for two features). The areas are formed like 2 moon crescents.

The 2-dim vectors describing the data points are associated with target values of “0” and “1” – with “0” describing the membership in the upper cluster and “1” the membership in the lower cluster of points. We see at once that any hyperplane – in this case a one dimensional, but curved line – separating the data clusters requires some non-linear description with respect to x1 and x2.

We also understand that we need some special plotting technique to visualize the separation. Within the named book of Geron we do not find a recipe for such a plot; however you do find example programs on the author’s website for the exercises in the book. Actually, I found some additional hints in a different book “Python Machine Learning” of Sebastian Raschka, pubished by PACKT Publishing, 2016. When I came to Geron’s discussion of the moon dataset I tried to combine it with the plotting approach of Raschka. For me – as a beginner – the whole thing was a very welcome opportunity to do some exercises with a Jupyter notebook and write some small functions for plotting decision regions.

I describe my way of dealing with the moon dataset below – and hope it may help others. However, before we move to Jupyter based experiments I want to give some hints on the SVM approach and Python libraries required.

SVM, “Polynomial Features” and the SVC Kernel

The algorithm behind a SVM performs an analysis of data point vectors in a multidimensional vector space and the distances of the points of different clusters. The strategy to find a separating hyperplane between data point clusters should follow 3 principles:

  • We want to guarantee a large separation in terms of metrical distances of outer cluster points to the separation plane.
  • We want to minimize the number of of points violating the separation.
  • The number of mathematical operations shall be reduced.

If we had some vector analysis a part of our education we may be able to figure out how to determine a hyperplane in cases where a linear separation is sufficient. We adjust the parameters of the linear hyperplane such that for some defined average the distance of carefully chosen cluster points to the plane is maximized. We could also say that we try to “maximize” the margins of the data clusters to the hyperplane (see the book of Raschka, cahpter 3, around page 70). We can moderate the “maximize” strategy by some parameters allowing for a narrower margin not to get too many wrongly placed data points. Thus we would avoid overfitting. But what to do about a non-linear separation surface?

Non-linearity can be included by a trick: Polynomial variations of the data variables – i.e. powers of the data variables – are added as new so called slack variables to the original data sets – thereby extending the dimensionality of the sets. In the extended new vector space data which previously were not separable can appear in clearly separated regions.

A SciKit-Learn Python class “PolynomialFeatures” supports variations in the combination of powers of the original data up to a limiting total power (e.g. 3). If the 2 dim data variables are x1 and x2, we may e.g. include x1**3, x2**3, x2*x1**2, x1*x2**2 and x1*x2 as new variables. On the extended vector space we could then apply a conventional linear analysis again.

Such a linear analysis is provided by a Python module called LinearSVC which is part of “sklearn.svm”, which provides SVM
related classes and methods. PolynomialFeatures is a class and part of “sklearn.preprocessing”.

Note that higher polynomials obviously will lead to a steep rise in the number of data operations as the dimensionality of the data sets grows significantly due to combinatorics. For many cases it can, however, be shown that it is sufficient to operate with the original data points in the lower dimensional representation space in a special way to cover polynomial extensions or distance calculations for the hyperplane determination in the higher dimensional and extended representation space. A so called “Polynomial Kernel” function takes care of this without really extending the dimensionality of the original datasets.

Note that there are other kernel functions for other types of calculation approaches to determine the separation surface: One approach uses an exponentially weighted distance (Gaussian kernel or RBF-kernel) between points in the sense of a similarity function to add new features (= dimensions) to the representation space. In this case we have to perform mathematical operations on pairs of data points in the original space with lower dimensionality, only.

Thus, different kinds of kernel functions are included in a SciKit-Learn class named “SVC“. All methods work sufficiently well at least for small and medium data sets. For large datasets the polynomial Kernel may scale badly regarding CPU time.

There is another kind of scaling – namely of the data to reasonable value regions. It is well known that data scaling is important when using SVM for the determination of decision surfaces. So, we need another Python class to scale our “moon” data reasonably – “StandardScaler” which is part of sklearn.preprocessing.

Required Python libraries

We assume that we have installed the Python packets sklearn, mathlotlib and numpy in the virtual Python environment for our ML experiments, already. (See previous articles in this blog for the establishment of such an environment on a Linux machine.)

We shall use the option of SciKit-Learn to build pipelines of operations on datasets. So, we need to import the class “Pipeline” from sklearn.pipeline. The “moons dataset” itself is part of the datasets library of “sklearn”.

All in all we will use the following libraries in the forthcoming experiments:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

from sklearn.datasets import make_moons

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import PolynomialFeatures

from sklearn.svm import LinearSVC
from sklearn.svm import SVC

Enough for today.In the next article

The moons dataset and decision surface graphics in a Jupyter environment – II – contourplots

we will acquire some basic tool skills in ML: I shall show how to start a Jupyter notebook and how to create contour-plots. We will need this knowledge later on for doing experiments with our moons data set and to visualize the results of SVM algorithms for classification.

Eclipse, PyDev, virtualenv and graphical output of matplotlib on KDE – III

During the last articles of this mini-series

Eclipse, PyDev, virtualenv and graphical output of matplotlib on KDE – I
Eclipse, PyDev, virtualenv and graphical output of matplotlib on KDE – II

we saw how to set up a basic PyDev project in Eclipse which we coupled to a virtual Python environment. We modified the PYTHONPATH and added Python packages to our project with the help of “pip”. In addition we have prepared the PyDev console for interactive Python experiments such that we can use matplotlib in a Qt5 environment. Thus we got a reasonably equipped development environment to start with Python based experiments in “Machine-Learning” [ML] – and build up code much more systematically than just with Jupyter notebooks.

One thing that we expect from an IDE (besides editors and code organization) is a possibility of debugging complicated Python modules and classes. So, in the final article of this series we shall have a brief look at (local) debugging of Python code within PyDEV. To set up a remote debugging server for any clients of our Linux machine is no major problem – but it is beyond the scope of this article.

As with the previous articles experienced Python and PyDev developers won’t learn anything new. My target group is students of ML and people intrested in Python who have some experience with other programming languages.

A “stupid” test example

We need a simple test code example to learn working with the debugging features of PyDev. We open our Eclipse installation and change to the PyDev perspective.

Watch out for the activated symbol at the right upper corner – here a list of different perspectives (which you have used recently) are displayed.

During the last article we had (within the PyDev-Explorer) already created a directory “mytestcode” below our basic project “ml_1”. By default settings of the PyDev environment, actually, we have created not only a directory but a so called Python “package” – which is a collection of modules that have a common context and should be delivered together. (You find more about “modules” and “packages” in any reasonable book on Python).

Now we add a (test) module – i.e. a file with some Python code – to our “package”.

In the next window (not shown we provide a name (without a “.py”-ending), e.g. “stupid” and in the subsequent window we choose a Template of type “<Empty>”. All these actions lead to the creation of a Python file “stupid.py” for which the Python editor of PyDev is opened:

Here we enter some “stupid” code:

def 
test(x=-1, y=-1):
    
    print("\nhelloworld: let us multiply and divide two POSITIVE INTEGERS < 10")
    if (x < 0):
        a = input("Give an integer value for a: ")
    else:
        a = x    
    if (y < 0):
        b = input("Give an integer value for b: ")
    else:
        b = y    
    print("a =" + a + ", b = " + b)
    
    if (a > 10):
        a=0 
    if (b > 10):
        b=0 
    
    c = int(a) * int(b)
    print("c=" + str(c))
    
    d = int(a) / int(b)
    print("d = " + str(d))
    return [c, d] 

Of course, you see already traps and lines doomed for failure; some of these traps we want to explore below by debugging :-). Debug As >> Other >> Python Run

By the way, we see a major disadvantage of the Eclipse environment (which does not only affect PyDEV): With “Oxygen” the outline view missed its capability to analyze beyond the first level of the code’s node hierarchy.

Our code does not contain any direct executable commands. We add 2 lines to be able to run it as a “Python Run” at the Python prompt of a console.

res = test()
print("c=" + str(res[0]) + ", d = " + res[1])

After this you may notice that the variable re is now displayed in the “outline view”.

Preparing debugging

Lets try to run our code:
Within the PyDev perspective we open a PyDev console for the interpreter of our virtualenv “ml1” (see the last article about how to do this). There we “import” our “stupid” code and answer the questions by typing in values for the variables a, b and pressing “Enter” each time. We end up with a first error message:

Not unexpected. Ok, lets turn to debugging. Eclipse offers us a special “perspective” which supports debugging. We open it by using the menu “Window >> Perspective >> Open perspective >> Other…“.

In the next window we choose “Debug”:

This leads to a change of the screen layout. The console view shows in the lower part of the screen that we have opened a “Debug console”. (You will see this in some of the following pictures).

There are a lot of new buttons available in the icon bar at the top. One in the middle shows a picture of a “bug” with an arrow besides it: obviously, there are options in what way to debug.

We now first click into our editor window for the “stupid” code and then on the arrow besides the “bug” button; you get something like this:

In my case there is a long list of previous debug runs.
However, in your case the list may be empty as you may never have launched any debug runs before.

As indicated in the screenshot you choose “Debug As >> Other >> Python Run” and click.

Unfortunately, the console view will now display an error message regarding “cython speedups”; this may look similar to the following:

I took the screen shot from another fresh installation – without a virtual Python environment… So the details of the message (especially the path to the interpreter) may look differently.

When I set up my PyDev environment with my virtualenv “ml1” the actual recommendation was to run the command

"/projekte/GIT/ai/ml1/bin/python3.6" "/projekte/eclipse/plugins/org.python.pydev.core_6.4.2.201807021651/pysrc/setup_cython.py" build_ext --inplace

The double quotes around the first 2 parts of the command are important in some command environments! On a Linux command console they do not do any harm.

The reason for this command is that we need to compile and install some additional cython related c-programs to get the “speedups” mentioned in the error message. It is not necessary to understand the details of this in our context.

What you have to do now is to start a Linux terminal window. There you move to the directory of your virtual Python environment – in my case to “/projekte/GIT/ai/ml1”. There you launch the command “source bin/activate” to activate the “virtualenv” with its interpreter.

Then you enter the following commands – Do NOT forget to activate the virtualenv “ml1” with the second command !

myself@mytux:~> cd /projekte/GIT/ai/ml1/
myself@mytux:/projekte/GIT/ai/ml1> source bin/activate
(ml1) myself@mytux:/projekte/GIT/ai/ml1> "/projekte/GIT/ai/ml1/bin/python3.6" "/projekte/eclipse/plugins/org.python.pydev.core_6.4.2.201807021651/pysrc/setup_cython.py" build_ext --inplace
running build_ext
building '_pydevd_bundle.pydevd_cython' extension
creating build
creating build/temp.linux-x86_64-3.6
creating build/temp.linux-x86_64-3.6/_pydevd_bundle
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -g -DOPENSSL_LOAD_CONF -fwrapv -fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -g -fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -g -fPIC -I/usr/include/python3.6m -c _pydevd_bundle/pydevd_cython.c -o build/temp.linux-x86_64-3.6/_pydevd_bundle/pydevd_cython.o
creating build/lib.linux-x86_64-3.6
creating build/lib.linux-x86_64-3.6/_pydevd_bundle
gcc -pthread -shared -flto -fuse-linker-plugin -ffat-lto-objects -flto-partition=none build/temp.linux-x86_64-3.6/_pydevd_bundle/pydevd_cython.o -L/usr/lib64 -lpython3.6m -o build/lib.linux-x86_64-3.6/_pydevd_bundle/pydevd_cython.cpython-36m-x86_64-linux-gnu.so
copying build/lib.linux-x86_64-3.6/_pydevd_bundle/pydevd_cython.cpython-36m-x86_64-linux-gnu.so -> _pydevd_bundle
running build_ext
building '_pydevd_frame_eval.pydevd_frame_evaluator' extension
creating build/temp.linux-x86_64-3.6/_pydevd_frame_eval
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -g -DOPENSSL_LOAD_CONF -fwrapv -fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -g -fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -g -fPIC -I/usr/include/python3.6m -c _pydevd_frame_eval/pydevd_frame_evaluator.c -o build/temp.linux-x86_64-3.6/_pydevd_frame_eval/pydevd_frame_evaluator.o
creating build/lib.linux-x86_64-
3.6/_pydevd_frame_eval
gcc -pthread -shared -flto -fuse-linker-plugin -ffat-lto-objects -flto-partition=none build/temp.linux-x86_64-3.6/_pydevd_frame_eval/pydevd_frame_evaluator.o -L/usr/lib64 -lpython3.6m -o build/lib.linux-x86_64-3.6/_pydevd_frame_eval/pydevd_frame_evaluator.cpython-36m-x86_64-linux-gnu.so                                                             
copying build/lib.linux-x86_64-3.6/_pydevd_frame_eval/pydevd_frame_evaluator.cpython-36m-x86_64-linux-gnu.so -> _pydevd_frame_eval                                            
(ml1) myself@mytux:/projekte/GIT/ai/ml1> 

 

After this it is reasonable to restart Eclipse via the menu point “File >> Restart“. if you first want to clean up the started Debug runs you could first click on the double red square icon in the console view’s icon bar:

and afterward on the double “x”-icon:

If you looked a bit to the leftmost window “Debug” of the debug perspective during these actions, you would have seen some changes there :-). What you did was to forcefully terminate and remove all launched debug runs from the debug environment!

Then restart by “File >> Restart”. Your Eclipse application should automatically start again – with the perspective and files open that you last worked with.

Breakpoints

Now we again click into the editor window for “stupid.py” and move the mouse over the bug-like icon in the top icon bar

and click. Now you should get something like the following – without errors for the debugger:

Our program code has started automatically due to the first of our final two commands (“res=test()”).

Note that the console has two parts – an upper one, where output from the code is shown and a lower part with a Python prompt.
The lower part is for interactive command execution during debugging. We ignore for the time being and only work in the upper part. This is, by the way, the area where the “input” command of Python prompts us to enter values.

There we enter 2 values for our variables a and b and … Of course, we run again into our first error. I have not displayed this as we would learn nothing new from it.

The reason, of course, is: We debugged without having set breakpoints before.

Setting breakpoints is fortunately easy: We move to our PyDev editor view. On the left side we see line numbers (if not: right click on the outer left border stripe of the editor view to get related options!). We right-click on the left side stripe of the line where we want to set a breakpoint to force a stop of the code execution there (i.e. before execution the line’s command).

Hint: Setting and removing breakpoints can also be done by left double-clicking on the leftmost black stripe of the editor.

We add 3 breakpoints for a start – at line 8, 17 and 19 :

Debugging

Our debugger should be in a state where we can just re-debug the present code. There are 2 possibilities for doing so: In the icon bar of the debug console view we can click on the green “Relaunch”-button:

Or you right-click on the relevant entry on the leftmost view on the last debug runs (which is only one right now) and choose the “Relaunch” option in the context menu:

Code execution stops at our first breakpoint – the correspondent line gets marked in the editor; in the “Variables View” on the upper right we see the values of the variables (x, y) set so far:

How to resume code execution?

This is where the different “player” icons in the top icon menu bar enter the game. You should explore these buttons in detail; for our introduction we just need the green “Resume“-button (and maybe also the red “Terminate“-button):

By clicking on the “Resume”-button code-execution is performed until the next break-point; we enter two integers for a and b again, and the code stops at line 17:

In the variables view we see now that a and b are strings! It is therefore that line 19 will produce an error – as we have learned before. We resume and end at line 19:

Resuming code execution now would again lead to an error. Let us, therefore, change the code, cast a, b to integers and save the modified file. However, this leads to an error message:

The debug environment lost track of our python code file.

Unfortunately, resuming now leads to our old error – despite the file changes:

Hint:
Whilst debugging as a Python run even reloading the “stupid”-module would not help.

Actually, what we do have to is to relaunch the whole debug process.

Whilst in an un-terminated debug session we can do this by simply pressing the red button with the yellow resume arrow:

Doing so and moving from breakpoint to breakpoint leads again into trouble – however now at a different code line, namely the final one (line 32):

You know,of course, why. But the code of our function test() in between has worked flawlessly now.

Instead of adding another breakpoint or performing the necessary code change for the last line, we try a different way of debugging – namely by linking a standard PyDev console to the debugger.

To clean up our workspace we terminate and remove all running debug runs first. We could use the buttons of the debug console for it as we have learned it above. But this time you could also try and use the leftmost “Debug View” and right-click on the relevant run there to get an option “terminate and Remove”.

Connect a PyDev command console to Debug sessions

We open “Window >> Preferences >> PyDev >> Interactive Console” and activate the checkbox for the point “Connect Console to a debug session?

Then we provoke a new error in our code at line 27 by changing it to:

d = a / int(b)

In addition we remove our last 2 lines

res = test()
print("c=" + str(res[0]) + ", d = " + res[1])

Thereby, importing module “stupid” in a console will NOT cause direct code execution 🙂 .

Let us start a PyDev console for the interpreter of our virtualenv (see the last article if you have forgotten how to do it). This gives us:

As expected we get a PyDev-console with a Python prompt.
The interesting thing, however, is indicated on the left screen side:

The entries in the “Debug View” show that we obviously started some debug run! Actually, our console is now part of a debugging session.

We can type anything at the prompt; we may e.g. define a variable “myvar”. Unfortnately it is not shown in the variables view, but we can get its value at any time when (and if) we have the prompt available :

As we are in an interactive environment we must import our module and do something with the test-function (as no code is executed automatically):

from 
mytestcode import stupid
res = stupid.test()

This brings us to our first break point:

We add a breakpoint to line 27, which will lead to an error – and march from breakpoint to breakpoint until we reach line 27:

Variable “a” is a string and would cause trouble. Let us change the code to “d = int(a) / int(b)” and save the modified file. We again get an error :

pydev debugger: Unable to find module to reload: "stupid".   

If we now resume code execution we will run into the foreseen trouble:

But afterwards we still have an active Python prompt of our Pydev-console for our session. We should be able to reload the code.

For Python 3.6 the following is required to do so:

By using the up-arrow then we move backward through the command history, start “res=stupid.test()” again and move via the break-points to the end – no errors any more (at least for the chosen a- and b-values):

Of course our file “stupid.py” contains a lot more sources for errors. E.g. there is no check or exception handling for a division by zero. And we have not used the function’s parameters, yet. I leave it to the reader to experiment with respective debugging.

How to use the prompt in the normal Debug console?

Now that you know the basics of how to debug, there is one more thing worth mentioning:

When you debug something as a Python Run in the standard “Debug Console”, you may have noticed the (green) Python prompt in the lower part of the console view:

What is it good for?

Well, with it you can interactively change variables and do other things interactively within the context of the debugged object (here of function test()).

Important note:
To avoid inconsistent states you should use the interactive prompt only, when the debugger stopped at a breakpoint. In addition you should enter values for variables requested by “input”-statements only in the upper part of the debug console!

But as soon as the debugger stops you can interactively ask e.g. for values of variables:

As soon as you press Enter in the lower part the
command is reflect in the upper part of the Debug console and results are shown there, too. So, while the debugger stops at a break point, you can do a lot of interactive things; you may set some new variables and use them later on – this is very useful when you want to keep some intermediate results for later purposes during a debug session.

Such new, interactively set variables will, by the way, even be shown in the Variables View (watch out for variables “alp” and “ufo”):

Customization of the debug environment

Eventually, a hint regarding customization of the environment discussed so far.

You should now have a look at all sub-points of the the two following menu points under “Window >> Preferences“:

Of course, there are the sub-points of “PyDev”:
There you can detect a lot more properties of e.g. the “Interpreters” (e.g. of your virtualenv environments) and e.g. the “Interactive Console“.

But Another interesting point is “Run/Debug“:
After a while you may want to use some of the properties there to make life easier during debug sessions. See e.g. for the length of the list of the last debug runs menu point “Run/Debug >> Launching >> Size of recently launched application list”.

Although I cannot discuss it in this article:
Another thing you should become familiar with is the configuration of Debug (and unittest) Runs.
You find a screen for it when you open the combobox of the main debug icon in the top icon bar of Eclipse and click on the point “Debug configurations …” there.

Unit tests

Interactively debugging is nice and useful. Something that is equally important in the long run is, however, “unit testing”. There are two interesting mechanisms that Python provides for doing this: The “doctest”-module and “unittest”. Both are beyond the scope of our introduction to PyDev. You find more about these things on the Internet or in reasonable books on Python.

Note, however, that Python’s “unittest” is especially interesting as the Debug environment of Eclipse/PyDev provides some nice views and tools for it. The following links may give you a first introduction:

https://www.youtube.com/watch?v=1Lfv5tUGsn8
https://www.youtube.com/watch?v=fU7RHewj6dg
https://www.blog.pythonlibrary.org/2016/07/07/python-3-testing-an-intro-to-unittest/

Conclusion

In this mini series of articles I have tried to show that setting up Eclipse with PyDev is relatively easy. This gives anybody interested in doing Machine-Learning-experiments with Python, Tensorflow and Keras a reasonable and cost free environment where you can build up and test solid code. A starting point could be module code which you export from a Jupyter notebook after some first trial experiments.

I will provide some examples for a two folded approach with Jupyter notebooks and code refinement via PyDev later on in this
blog.

One may ask now, why do we need Jupyter notebooks at all for machine learning experiments. Well, one big advantage of a Jupyter environment is the fact that we can arrange blocks of Python commands in a cell and re-execute the code-blocks in a self-chosen order again. So, it is possible to redo experiments quickly in a modified way; Jupyter gives you – in my opinion – a bit more flexibility in executing preliminary code fragments in a quick and (admittedly) dirty way. In PyDev you would have to work with editors and maybe multiple files to something similar.