Here are a few suggestions for Chapters 2 and 3:

  • Assuming that the reader is not expected to have come across trigonometric functions before, maybe you should expand on them a little bit. Explicitly discussing the sign in different quadrants with a schema illustrating them might be of value.

  • The identity sin^2 x + cos^2 x = 1 should be mentioned.

  • You might include the law of sines and cosines for triangles. Granted, you might never need them in programming, but they nicely stress the value of the trigonometric functions.

  • This might be a controversial suggestion, which applies to future chapters as well: Include proofs. In chapter 3, proofs would be good for the identities u v = |u| |v| cos(theta) and |u X v| = |u| |v| sin(theta). After all, being able to prove theorems is one of the things that make mathematics so cool (at least for some). If (understandably) you do not want to add proofs, maybe you could provide links to good and easy to understand proofs. For example, you might include for the cross product formula.

  • You should stress the fact that u X v != v X u. (Sorry if you did and I just ignored it.)

  • It seems that the API for injecting global CSS has changed in version 4 of styled components; see
    In Exercise 1 of the section "What CSS in JavaScript is not" the answer "Red" is marked as being the right one. But, as confirmed by the explanation, the correct answer is actually "Blue".

    Similarly, in Exercise 2 of that section the answer "Yes" is marked as being the right one, although "No" is the correct answer.
    The type definitions in Section 3.3 (Circular References) contains args fields in places where I suspect they might not be necessary. For example,

    const linkType = new GraphQLObjectType({
      name: 'Link',
      fields: () => ({
        id: { type: GraphQLInt },
        url: { type: GraphQLString },
        description: { type: GraphQLString },
        author: {
          type: userType,
          args: {
            author: { type: GraphQLInt },
          resolve: ({ author }) => find(users, { id: author }),
        comments: {
          type: new GraphQLList(commentsType),
          resolve: ({ comments }) => => find(commentsList, { id: i })),

    probably can be simplified to

    const linkType = new GraphQLObjectType({
      name: 'Link',
      fields: () => ({
        id: { type: GraphQLInt },
        url: { type: GraphQLString },
        description: { type: GraphQLString },
        author: {
          type: userType,
          resolve: ({ author }) => find(users, { id: author }),
        comments: {
          type: new GraphQLList(commentsType),
          resolve: ({ comments }) => => find(commentsList, { id: i })),

    Similarly, the arguments of some of the resolvers might be incorrect.
    When running the code for Section 3.7 ("Making it work in Tensorflow") I get various deprecation (and other) warnings.

    Also, I wonder whether using Tensorflow is the best option here, or whether scikit-learn might ultimately be the easier choice (assuming it offers a linear classifier)?
    It is really great that you show in chapter 2 how things can go pear-shaped if you don't choose the correct step size / error normalisation.

    But I wonder whether the approach taken in really is the best one: Judging from the print output, the weight values do not seem to converge...

    So maybe instead of normalising the error, you might consider normalising the article lengths and shares by dividing them by 1000, so that the values are in the interval [0, 1]. Then the weights converge rapidly (in less than 10,000 steps), and the resulting prediction model seems to be okay as well.
    There might be a subtle issue in the code for solving the missionaries and cannibals problem: When (motivated by Exercise 3) I change MAX_NUM to, say, 5, doesn't seem to stop. The reason seems to be that the in operator (used for checking whether a state is in the explored list already) is unreliable for MCState. Adding an __eq__ and a __hash__ method seems to solve the issue.

    (And while I know that "missionaries and cannibals" is a standard name, I personally think that basing the problem on missionaries and cannibals is unfortunate, as (although this is certainly not intended) it ultimately makes use of the rather colonial stereotype of noble (white?) missionaries vs savage (black?) indigenous people.)
    I'm a bit puzzled by the conditions ml.row - 1 > 0 and ml.column - 1 > 0 in listing 2.15. Shouldn't these be ml.row - 1 >= 0 and ml.column - 1 >= 0?
    Thanks for the gentle introduction to the Open API Specification in Chapter 4!

    I think the chapter would gain considerable value if you dedicated more space on the OAS ecosystem in general, and Swagger UI in particular. The Swagger editor is really impressive, but at the end of the day, you want to host your API documentation somewhere. This may be a crucial consideration when deciding whether to use OAS, and if you are a careless reader like me it is quite easy to miss the importance of Swagger UI in this respect. So including a section on showing how to get it running (and what it's doing) would be great.

    Also, editing files in a text editor or IDE obviously has advantages over editing them a browser. As such advice on plugins for editing OAS v3 files in some editors and IDEs should be very welcome.
    Thanks for the link to the calculator project. As you asked for my opinion, here are my two cents: Having a chapter about building the calculator might be a good idea. Having a step by step guide for converting a React project probably wouldn't. I'm skeptical of the latter, as a fair share of readers might never have used React, and even if they have, they might not be overly keen on converting existing code.

    Having said this, a "cheat sheet" (possibly in an appendix) for translating functionality between web components and frameworks like React (or Vue, or Angular) could be of good value. Readers who have used frameworks before might wonder whether features like declarative code, data binding (in particular with arrays) or scripts for easily creating, running and testing projects are available with web components as well (and how).

    From an advertising point of view you might consider mentioning hyperhtml/lit-html in Chapter 1. Knowing that declarative code is possible with web components might help convincing web framework users that reading the book would be of value.
    There is a minor subtle issue in Section 3.2.3. You suggest to set the apiKey and searchTerm properties in the script tag. But as the script tag is in the header, the poly-search tag does not exist in the DOM yet, and setting the properties will give an error.
    Is there a link to the step by step project already? (I've tried, but I could only find an index.html file there.) Thanks!
    There might be a fourth option: use the old way, but point out that there is a new way and that it will be explained in Chapter N. smilie
    I know this is nit-picking (and a matter of taste), but it might be worth using a template literal when setting this.innerHTML.
    Thanks! I'm looking forward to that (and all the other chapters, of course).
    It makes sense to focus the book on one API design principle (i.e. REST). But I think it would add value to the book if you added a (brief) comparison to other major current design principles. Maybe you could add an appendix listing design principles such as gRPC or GraphQL with their weaknesses, strengths, typical use cases and links to further information?
    While I can understand that you don't want to overcomplicate the content in Chapter 3, I feel a bit uneasy about the fact that content type and HTTP status aren't mentioned in Chapter 3. I guess you will discuss them elsewhere in the book, but even so I feel you should warn the reader that there are such things and that they are important.
    The unit tests in Section 8.5.2 about pagination don't cover all edge cases, and it would be tempting to add more tests. But this immediately leads to the question what would be the best strategy for doing so. In other words, it might be of benefit if the chapter touched on parametrized tests. And while this arguably is beyond the scope of the book, it might also be of value if you used the opportunity to point the reader to a JavaScript equivalent of Haskell's QuickCheck or Python's Hypothesis.
    It might be worth pointing out the difference between mockImplementation and mockImplementationOnce in Section 4.5.1. (Given that mockImplementation introduces a side effect into the unit tests, would it perhaps make sense to discuss this and to outline how to deal with it?)
    You state that the unit test in listing 4.10 doesn't pass until you've added the code in listing 4.11. But that doesn't seem to be correct, as you already call $bar.start in listing 4.7.
    I have three small suggestions for chapter 2.

    1. While discussing the nitty-griity of setting up a Vue project (and linting/testing) is a really good idea, it might be worth to also explain how to do all this with the Vue CLI.

    2. Given that you mention the ease of debugging with WebStorm in Appendix A, you might perhaps mention in passing in chapter 2 that you might be able to use your IDE for debugging Jest tests.

    3. Maybe you could mention the --watch option for Jest?
    It seems I was indeed blind... Sorry for the hassle caused.
    Just as small suggestion: Even though servers running on macOS may be uncommon, readers (or at least this reader...) might still want to test HTTP/2 on a Mac. As such, it would be great to include some instructions for macOS in Appendix A, even if it's only by providing links to instructions such as
    Don't you have to explicitly emit a "load all messages" event in Section 31.3 for the existing messages to be loaded from the server? (My apologies if the code for this exists and I just didn't spot it.)
    There are some minor issues with listing 30.4:

  • The file name chatController.js in the title ("Adding a chat action in chatController.js") is incorrect.

  • The displayMessage function expects an object { content: ... } as argument, but actually a string is passed.

  • The displayMessage function adds a li element to a div (not ul or ol) element, which presumably results in invalid html.
  • I'm a bit puzzled by the order of (imported) routes in Listing 26.2. Shouldn't the error routes be imported last, as they might contain a middleware function for catching page not found errors?
    That might be nitpicking, but it would be helpful if you told the reader that listing 24.6 won't work before the listing... smilie Also, is there a reason why you refer to locals.loggedIn / locals.currentUser rather than just loggedIn / currentUser?

    On a slightly related note, it might be a good idea to warn the reader that you will lose all login information once you restart the server.
    It might be worth pointing out that the API of express-validator has changed from version 3 to 4. The new API does not make use of methods added to the req object, it seems (cf.
    The text notes that "[a] pre hook on save is run anytime the user is saved. That means on creation and after an update.". This might be a bit misleading, as it is not called when using update or findOneAndUpdate. Quoting from "Pre and post save() hooks are not executed on update(), findOneAndUpdate(), etc. You can see a more detailed discussion why in this GitHub issue. Mongoose 4.0 has distinct hooks for these functions."
    Ah, sorry, I see you are discussing this later in the chapter. But maybe you could warn the user beforehand, to avoid frustration? smilie
    Listing 20.3 suggests to set the method of the form element to "PUT". However, this is invalid HTML (cf., and both Safari and Chrome issue a GET instead of a PUT request.
    Just a suggestion: As Node supports async/await, it might be worth including this in Chapter 17.
    The wording in the paragraph above listing 15.1 suggests that the getAllSubscribers method returns an array of subscribers. But given that this array is returned in a callback, doesn't the method just return undefined?
    The database is not passed as an argument to the callback function of MongoDB.connect; the MongoDB client is. So (based on the code probably should look something like the following.

    const MongoDB = require('mongodb').MongoClient;
    MongoDB.connect('mongodb://localhost:27017', (error, client) => {
        if (error) throw error;
        const db = client.db('csa_app_db');
        db.collection('contacts').find().toArray((error, data) => {
            if (error) throw error;
    At the end of Section 13.1 you suggest to test the Mongo installation by executing the mongo command from a terminal. However, as the Mongo server has not been started yet at this stage, you will get an error message when doing this.
    Thanks! - On a related note, you might make the section(s) on static files a bit clearer if you stressed that setting the directory with express.static does not affect sendFile, and if you pointed out how to use path.resolve when using setFile in a controller.
    Just a minor, probably irrelevant question: Is there a reason why in Listing 7.8 you deviate from your convention to use a payload key in an action?
    I'm a bit puzzled about the "next" in Listing 14.4, which doesn't seem to be defined.
    It might be worth pointing out that one should use the useMongoClient option with Mongoose's connect command; see
    It seems that the error handler function in the error controller (Listing 12.10) needs four (rather than three) arguments in order to work properly:

    exports.internalServerError = (errors, req, res, next) => {
      console.log(`ERROR occurred: ${errors.stack}`)
      res.send('500 | Sorry, our application is taking a nap!');

    The use of __dirname inListing 11.5 suggests that __dirname is (as stated in the text) equal to the application folder. However, according to, it is equal to current module folder.
    While in this example it shouldn't make a difference, one might consider passing a function to setState in the toggleForm method in listing 2.12, in order to avoid issues caused by the asynchronous nature of setState (cf.

    toggleForm = (e) => {
        this.setState((prevState) => (
                    showNewCardForm: !prevState.showNewCardForm

    Thanks. Much appreciated!
    On page 76 you state that

    This middleware function allows you to run custom code on the request before it matches with any other routes in your application.

    This sentence might be slightly ambiguous and could be interpreted as (wrongly, it seems) implying that the order of use and get doesn't matter, as the middleware function is executed before Express tries to match the route.
    I'm a bit puzzled by the setting of the domain of the color scale for the iris plot. You point out that the reason for explicitly setting the domain is to avoid indeterminate behaviour. However, doesn't the expression still depend on the ordering within the csv file, so that effectively the problem persists?
    A fairly general suggestion: Judging from the table of content, the course won't go into testing D3 code. But perhaps it might be a good idea to at least provide some pointers as to where the interested viewer can find information about good testing strategies.
    Just a minor suggestion: It might be helpful to point out in Module 1 how to set up an http server on your computer.
    Just a small suggestion: Perhaps you could add npm docs to your list of npm commands in Section 3.2? It's such a useful command after all...
    It might be worth pointing out in Chapter 3 that in recent versions of Node the --save flag has been renamed to --save-prod for npm install, and that the default behaviour of npm now is to save an installed package as a (production) dependency in package.json.
    You state in the sidebar at the beginning of Chapter 2

    To test whether this code works, you could save it in a .js file, link it to an .html webpage, and run that file in a browser, viewing the results in your browser’s inspector window. With Node.js, you get immediate satisfaction by running JavaScript files directly in terminal.

    It would be great if you could also discuss unit testing (and maybe even end-to-end testing). Of course this may not be a topic for Chapter 2, but I think the book will greatly benefit if you explain (automated) testing of the concepts you introduce.
    I'm a bit puzzled by listing 1.1. Why do you define the Goat class as

    let Goat = class GoatClass {
            console.log(`I love eating ${foodType}`);

    rather than using

    class Goat {
            console.log(`I love eating ${foodType}`);

    It might be worth discussing the return value of the function returned by asynchronous action creators in Chapter 4. (Unit testing with redux-mock-store seems to require that it isn't a void function but actually returns a promise.)
    Ah, my apologies. I see you are adding the missing details in your editTask action creator.
    You are using a PUT request for the editTask function in Listing 4.11. However, judging from Listing 2.22, the params object passed to the function only contains a subset of the task details (namely the status). json-server seems to drop the missing details in the database file, so that you are left without title or description. Would it make more sense to use a PATCH request instead?
    When I start json-server with the db.json file content given in Listing 1.1, I get an error:

    Data must be an object. Found object.See for example.

    It seems that you have to modify the content to be an object:

      "tasks": [
          "title": "Learn Redux",
          "description": "The store, actions, and reducers, oh my!",
          "status": "Unstarted"
          "title": "Peace on Earth",
          "description": "No big deal.",
          "status": "Unstarted"
          "title": "Create Facebook for dogs",
          "description": "The hottest new social network",
          "status": "Completed"

    It is perfectly reasonable that you discuss hot module replacement (HMR) in the context of using Create React App. However, I think it would be great if you could also briefly discuss what to do if you want to use HMR without Create React App (such as by adding a paragraph with a link to a web page explaining the procedure).
    It seems that you have to replace

                const tasks = tasks.filter(task => task.status === status)
                return (


                const tasks1 = tasks.filter(task => task.status === status)
                return (

    I'm a bit puzzled: Don't you have to bind the methods onTitleChange, onDescriptionChange, onCreateTask and toggleForm to this in Listing 2.12?
    It might be worth noting that React.PropTypes has been deprecated in React 15.5.
    There are few minor changes which might make it easier to get started with service workers.

  • You could point out which browsers support (or don't support) service workers. There are readers who are using Safari, after all... smilie

  • You mention that you can use localhost for testing, but you might state (more) explicitly that http can be used in this case. (You might also mention that even for localhost the files must be served via http - file:// doesn't seem to work.)

  • It would be helpful if you stated clearly that listing 1.2 is a complete service worker file. You call it a "snippet of code inside the service worker file", and together with the fact that the variable self isn't declared this (erroneously) might suggest to the reader that they cannot use that code for getting their feet wet with service workers.

  • Section seems to be a bit ambiguous regarding the definition of "term frequency". On the one hand, the main text suggests that it is the ratio of the number of occurrences of a word and the total number of words. On the other hand, the Python code suggests it is the ratio of the number of occurrences and the number of distinct words.

    For example, if you take the sentence "the faster Harry is faster", the main text suggests that TF("faster") == 2 / 5, and the Python code suggests TF("faster") == 2 / 4.
    The nb_epoch argument of the fit() method for a Keras model has been renamed to epochs.
    I have to confess I didn't pay too much attention to the piano player example. I'm not convinced it adds terribly much clarity, but your mileage may vary, of course.

    However, there is a question that does spring to mind when you discuss the sheer size of the matrix of word vectors: Why don't you just store a number ("the word is the n-th entry in your vocabulary") instead of a one-hot vector?

    I indeed copy-and-paste (or type) at least some of the examples. I'd say that if you include the Python output, you should rather keep the ">>>" prompts for the sake of clarity.
    Shouldn't the line

    wordvecs = np.zeros((len(vocab), len(vocab)), int)


    wordvecs = np.zeros((len(sentence.split()), len(vocab)), int)
    While the error indeed converges to zero for the code given at the end of chapter 6, this doesn't necessarily seem to be the case when choosing a value other than 1 for np.random.seed(). (For example, for the seed value 2 it seems to converge to 1.)
    The code at the beginning of the section "Breaking Gradient Descent" contains a variable deriv, which hasn't been defined. It seems to be a typo and should read delta instead.
    Section 5.2.3 (p. 66) states that the usage of the PhoneNumberPipe with parameter is

    {{ <phone-number> | phoneNumber | <format> }}

    but this should read

    {{ <phone-number> | phoneNumber : <format> }}

    Section 2.2.10 states that a tweet can be encoded as a 2D tensor of shape (140, 128). But wouldn't you rather encode it as a 1D tensor of shape (140), like ['T', 'h', 'i', 's', ' ', ...]?
    The URL in

    <script src="" type="text/JavaScript"></script>

    seems to return a HTML page rather than a JavaScript file.

    It seems that "key =>" is missing in line 3 of listing 4.12.
    I'm still a bit puzzled. I wouldn't understand "bumping it up the DOM" to mean "moving it to the end of the list of siblings", which is what raise() does according to the API.
    Figures 4.5 and 4.6 and the related discussion seem to refer to version 3 of DS3; version 4 comes with a default stylesheet rendering the axes legible without further customisation (see

    On a related note, the axis functions accept a scale, so (for example) d3.axisLeft().scale(yScale) can be shortened to d3.axisLeft(yScale).
    If I understand your code for populating the modal dialog in Section 3.3.2 (p. 109) correctly, you rely on d3.values returning an array of values which have the same order as the values in the csv file (i.e. the team comes first, the region second etc.). However, according to the API ( the order of values in the array returned by d3.values is undefined. So could it happen that the dialog is populated incorrectly?
    You state on page 105 that the quantize scale was discussed in chapter 2, but I cannot remember having seen it there (and a text search turned up no trace either).
    The explanation of the raise and lower functions on page 99 is a bit ambiguous: Does "bump your object to the top of its siblings in the DOM" mean that it is moved to the top in the DOM tree or that is moved to the top in the display?
    It might be worth mentioning event namespacing when discussing the on() function on page 94. (When I was reading Section 3.2.3, I added a second on() instead of updating the highlighting function, and was left wondering why my first on() was ignored.)
    Is it really necessary to set the text anchor in soccerviz.js (listing 3.3), given that you have a rule for it in the stylesheet (listing 3.1)?
    You state in the main text and the caption of Figure 2.26 that this figure shows the <text> labels reset to match the new array values. But it seems that the figure shows the old labels.
    I'm a bit puzzled by Figure 2.24. Shouldn't there be four (not two) green DOM element circles in the top left panel? And shouldn't "selection" be called something like "binding"? And maybe the selection/binding should also be shown for the still existing DOM nodes in the panels on the right hand side?
    You state in Section 2.1.3 that Date.parse casts a string into a date datetype. This suggests that it returns a Date instance, whereas it actually returns the number of milliseconds elapsed since January 1, 1970, 00:00:00 UTC.
    Yes, the font size is updated if you reload the page after resizing the browser.
    Happy New Year 2017!

    I know, it's a bit pedantic, but the wording of the test description "The Contact class should have a valid constuctor [sic]" in listing 2.7 arguably suggests that you need the empty constructor in the definition of Contact. But leaving out the constructor is perfectly fine:

    export class Contact {
    (This is an issue which will probably be fixed in a future release of angular-cli, but I post it in case someone else runs into the same problem.)

    Having created a new project with Angular-CLI and having added a unit test, I was surprised that ng test failed with the error that 0 tests were found. The reason for this turned out the one discussed at

    The workaround is to replace the code

        singleRun: false


        singleRun: false, 
        mime: {
            'text/x-typescript': ['ts','tsx']

    in the Karma configuration file (karma.conf.js)
    There seem to be two small issues in Listing 2.1:

    1. If I followed the text correctly the node_modules folder and the test runner should both be in the ta2a directory, so that the paths for the JavaScript files should start with './node_mnodules' rather than '../node_modules'.

    2. Some of the files paths contain non-ASCII dashes.
    One small thing that might be worth mentioning in the subsection on horizontal overflow is the overflow-text property.
    If you use the CSS from listing 2.19

    :root {
      font-size: calc(0.5em + 1vw);

    Safari (10.0.2) doesn't update the font size when you change the viewport size. Maybe it might be worth pointing this out in the discussion of calc()?
    Thanks for writing such a nice book on RxJS. I hope you don't mind if I make a few suggestions, though.

    1. You are demanding quite a bit of patience from your readers, given that they have to wait until Chapter 3 before they get to see the first RxJS code. It might make sense to whet the appetite by including a small RxJS program in the first chapter. The drag-and-drop example might be a good candidate.

    2. While I can understand that you don't want to re-invent the wheel and duplicate the RxJS reference on all the operators, I still feel that including a list of all operators might make sense. It increases the chance that readers actually take note of what is available out there...

    3. I think you could spend more time on explaining how to model data and data interactions. Another book on RxJS develops a simple arcade game as an example (where a hero can shoot alien spaceships and vice versa), and an example like that might help to wrap your mind round a larger RxJS project.

    4. The first RxJS construct to be used in a book on Angular 2 I'm reading happens to be a BehaviorSubject. As such I think it would make good sense to include subjects in the book (rather than just providing links to documentation), and there would be no harm in doing this earlier than in chapter 8.

    5. It would be great if the installation instructions include some notes on npm, TypeScript etc. It might, for example, help to point out "import 'rxjs'" makes all operators available, and that you can install the TypeScript definitions for Ramda with "npm install @types/ramda".

    Again, thanks for the book. I'm looking forward to the next chapters.
    Thanks! The plugin for Chrome works fine, but for whatever reason the Firefox plugin didn't seem to make a difference.
    The wording

    Our server side will be a simple TCP application [...] using the Node.js WebSocket API

    in section 8.2.2 (page 220) suggests that Node.js ships with WebSockets support out of the box. But that doesn't seem to be the case. Maybe you could mention what external library to install (websockets, I assume)?
    It might help if section 5.8 included marble diagrams like those in for showing the difference between mergeMap and concatMap, in particular as the DnD example seems to work with mergeMap as well.

    On a similar note, I found figure 5.10 somewhat confusing. A diagram like that on would make the meaning of switch() much clearer, I think
    The AJAX call in listing 5.3 seems to fail, as the Wikipedia API doesn't allow the cross-site request.
    I have some questions regarding listing 4.5.

    1. Why don't you use -y log( h ) - (1 – y) log( 1 – h ) as the cost function?

    2. Shouldn't the minus sign be omitted in y_model = tf.sigmoid(-(w[1] * X + w[0])) ?

    3. I tried using the code with all training x values shifted by 3. I would expect the code to find a w[0] close to -3, but that doesn't seem to be the case. Why?
    You state as the key takeaway that the slope points always to the bottom of the bowl. But it actually points away, as the gradient is negative to the left and positive to the right of the bottom.
    The solution for finding the oldest member of the IT department seems to have a small logical error: If the first employee is older than any IT member, that employee will be returned.
    While you clearly state that there are ways other than Docker to install TensorFlow, it might be worth mentioning explicitly that installing can be as simple as running "pip install tensorflow".
    Here are some suggestions for chapter 3. (I hope my remarks don't sound too negative!)

  • The aim of the book is to teach machine learning. But then, I think, it should explain the algorithms used. Hence just providing example code with the GrdientDescentOptimizer function is insufficient - you should actually describe what this function is doing. (I'm not saying you should provide all the gory mathematical details, but you should at least give the qualitative picture - and maybe a link to the details). In other words, you shouldn't (only) provide cookbook recipes, but give the reader an understanding of why these work.

  • You should explain the meaning of the learning rate and learning epochs (and how to choose reasonable values for them).

  • One thing which eluded me until the very end of the chapter (when I stumbled across the reduce_sum function and had to find out its meaning [which you might want to explain]) was that the model and cost actually operate on vectors, not scalars. It might be worth emphasizing this fact. Going through the algorithm of the Python code with mathematical formulae might help. (It might also help to expand on placeholders in chapter 2 (as in

  • The main text and the Python code differ regarding the cost function for regularisation. Also, the statement "The choice of norm depends case by case" leaves the reader (or at least this reader) wondering how to figure out which norm to choose.

  • Maybe a plot of final_cost over lambda instead of (or in addition to) Figure 3.11 might be instructive, in particular if it includes an explanation of why it looks the way it does.

  • You might expand on the concept/definition of "bias" and "variance" in section 3.1.1.

  • You start chapter 3.1 with the statement "If you have a hammer, every problem looks like a nail." In this spirit, it might add value to the chapter if you give an example or two of where linear regression is *not* the appropriate tool - not everything is a nail, after all. smilie

  • You refer to real data at the end of the chapter. But perhaps it might be instructive if you actually include a real world example in the chapter.

  • I'm not 100 % convinced that using linear regression as the first example for a TensorFlow program is the ideal choice. After all (although this might be subjective) fitting a polynomial to a set of data points isn't the most exciting thing ever. The TensorFlow website starts off by interpreting handwritten digits, whicvh looks more interesting to me. I'm not suggesting to change the order of chapters (linear regression is a reasonable starting point, I guess), but maybe you could include a "teaser" (like the one on the TensorFlow site) at the end of chapter 2.
  • Judging from the program logic and comparing with Listing 3.3, I suspect that the line

    w_val =

    shouldn't be indented in Listing 3.2.
    It seems that when using the save method (as in Listing 2.10) two files are generated, namely a *.ckpt' and a *.ckpt.meta one. Maybe you could explain in section 2.7 what the meta file is for?
    I feel that some points used in exercises 2.1 and 2.2 might be worth being explained in the main text:

  • Multiplying a tensor A by a scalar s returns the tensor you get when multiplying all components of A by s.

  • Subtracting a scalar s from a tensor A returns the tensor you get when subtracting s from all components of A.

  • Exercise 2.2 also raises some questions:

    1. Is there a reason why you use
    rather than just -A?

    2. What should you use for x? (Something like
    x = tf.convert_to_tensor(np.linspace(...))

    3. Why do you use
    tf.pow(sigma, 2.0)
    rather than

    4. Shouldn't you use

    Finally, I think that referring to Figure 2.3 makes exercise 2.2 harder rather than easier.