text:
"""Builds the Adience network.

Summary of available functions:

 # Compute input images and labels for training. If you would like to run
 # evaluations, use input() instead.
 inputs, labels = distorted_inputs()

 # Compute inference on the model inputs to make a prediction.
 predictions = inference(inputs)

 # Compute the total loss of the prediction with respect to the labels.
 loss = loss(predictions, labels)

 # Create a graph to run one step of training with respect to the loss.
 train_op = train(loss, global_step)
"""
# pylint: disable=missing-docstring
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import gzip
import os
import re
import sys
import tarfile

import tensorflow.python.platform
from six.moves import urllib
from six.moves import xrange    # pylint: disable=redefined-builtin
import tensorflow as tf

import adience_input
from tensorflow.python.platform import gfile

ad_input = adience_input.DataInput()
ad_input.read_from_txt()

FLAGS = tf.app.flags.FLAGS

# Basic model parameters.
tf.app.flags.DEFINE_integer('batch_size', 32,
                            """Number of images to process in a batch.""")
tf.app.flags.DEFINE_string('data_dir', 'data/aligned',
                             """Path to the CIFAR-10 data directory.""")

# Process images of this size. Note that this differs from the original CIFAR
# image size of 32 x 32. If one alters this number, then the entire model
# architecture will change and any model would need to be retrained.
IMAGE_SIZE = 64

# Global constants describing the CIFAR-10 data set.
NUM_CLASSES = 2

NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN = 0 #change it when reading input data (in distorded inputs)
NUM_EXAMPLES_PER_EPOCH_FOR_EVAL = 0

# Constants describing the training process.
MOVING_AVERAGE_DECAY = 0.9999         # The decay to use for the moving average.
NUM_EPOCHS_PER_DECAY = 350.0            # Epochs after which learning rate decays.
LEARNING_RATE_DECAY_FACTOR = 0.1    # Learning rate decay factor.
INITIAL_LEARNING_RATE = 0.1             # Initial learning rate.

# If a model is trained with multiple GPU's prefix all Op names with tower_name
# to differentiate the operations. Note that this prefix is removed from the
# names of the summaries when visualizing a model.
TOWER_NAME = 'tower'

def _activation_summary(x):
    """Helper to create summaries for activations.

    Creates a summary that provides a histogram of activations.
    Creates a summary that measure the sparsity of activations.

    Args:
        x: Tensor
    Returns:
        nothing
    """
    # Remove 'tower_[0-9]/' from the name in case this is a multi-GPU training
    # session. This helps the clarity of presentation on tensorboard.
    tensor_name = re.sub('%s_[0-9]*/' % TOWER_NAME, '', x.op.name)
    tf.histogram_summary(tensor_name + '/activations', x)
    tf.scalar_summary(tensor_name + '/sparsity', tf.nn.zero_fraction(x))

def _variable_on_cpu(name, shape, initializer):
    """Helper to create a Variable stored on CPU memory.

    Args:
        name: name of the variable
        shape: list of ints
        initializer: initializer for Variable

    Returns:
        Variable Tensor
    """
    with tf.device('/cpu:0'):
        var = tf.get_variable(name, shape, initializer=initializer)
    return var

def _variable_with_weight_decay(name, shape, stddev, wd):
    """Helper to create an initialized Variable with weight decay.

    Note that the Variable is initialized with a truncated normal distribution.
    A weight decay is added only if one is specified.

    Args:
        name: name of the variable
        shape: list of ints
        stddev: standard deviation of a truncated Gaussian
        wd: add L2Loss weight decay multiplied by this float. If None, weight
                decay is not added for this Variable.

    Returns:
        Variable Tensor
    """
    var = _variable_on_cpu(name, shape,
                                                 tf.truncated_normal_initializer(stddev=stddev))
    if wd:
        weight_decay = tf.mul(tf.nn.l2_loss(var), wd, name='weight_loss')
        tf.add_to_collection('losses', weight_decay)
    return var

def _generate_image_and_label_batch(image, label, min_queue_examples):
    """Construct a queued batch of images and labels.

    Args:
        image: 3-D Tensor of [IMAGE_SIZE, IMAGE_SIZE, 3] of type.float32.
        label: 1-D Tensor of type.int32
        min_queue_examples: int32, minimum number of samples to retain
            in the queue that provides of batches of examples.

    Returns:
        images: Images. 4D tensor of [batch_size, IMAGE_SIZE, IMAGE_SIZE, 3] size.
        labels: Labels. 1D tensor of [batch_size] size.
    """
    # Create a queue that shuffles the examples, and then
    # read 'FLAGS.batch_size' images + labels from the example queue.
    num_preprocess_threads = 16
    images, label_batch = tf.train.shuffle_batch(
            [image, label],
            batch_size=FLAGS.batch_size,
            num_threads=num_preprocess_threads,
            capacity=min_queue_examples + 3 * FLAGS.batch_size,
            min_after_dequeue=min_queue_examples)

    # Display the training images in the visualizer.
    tf.image_summary('images', images)

    return images, tf.reshape(label_batch, [FLAGS.batch_size])

def distorted_inputs():
    """Construct distorted input for CIFAR training using the Reader ops.

    Raises:
        ValueError: if no data_dir

    Returns:
        images: Images. 4D tensor of [batch_size, IMAGE_SIZE, IMAGE_SIZE, 3] size.
        labels: Labels. 1D tensor of [batch_size] size.
    """
    # filenames = [os.path.join(FLAGS.data_dir, 'cifar-10-batches-bin',
    #                                                     'data_batch_%d.bin' % i)
    #                            for i in xrange(1, 5)]
    # for f in filenames:
    #     if not gfile.Exists(f):
    #         raise ValueError('Failed to find file: ' + f)

    #ad_input.read_adience()
    #change if you want to go to cross-fold
    #
    global NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN
    NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN = len(ad_input.train_string_que)

    # Create a queue that produces the filenames to read.
    #filename_queue = tf.train.string_input_producer(filenames)

    # Read examples from files in the filename queue.
    read_input = ad_input.read_adience()
    reshaped_image = tf.cast(read_input.dec_image, tf.float32)

    height = IMAGE_SIZE
    width = IMAGE_SIZE

    # Image processing for training the network. Note the many random
    # distortions applied to the image.

    # Randomly crop a [height, width] section of the image.
    distorted_image = tf.image.random_crop(reshaped_image, [height, width])

    # Randomly flip the image horizontally.
    distorted_image = tf.image.random_flip_left_right(distorted_image)

    # Because these operations are not commutative, consider randomizing
    # randomize the order their operation.
    distorted_image = tf.image.random_brightness(distorted_image,
                                                 max_delta=63)
    distorted_image = tf.image.random_contrast(distorted_image,
                                                lower=0.2, upper=1.8)

    # Subtract off the mean and divide by the variance of the pixels.
    float_image = tf.image.per_image_whitening(distorted_image)

    # Ensure that the random shuffling has good mixing properties.
    min_fraction_of_examples_in_queue = 0.4
    min_queue_examples = int(NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN *
                             min_fraction_of_examples_in_queue)
    print ('Filling queue with %d Adience images before starting to train. '
                 'This will take a few minutes.' % min_queue_examples)

    # Generate a batch of images and labels by building up a queue of examples.
    return _generate_image_and_label_batch(float_image, read_input.label,
                                            min_queue_examples)

def inputs(eval_data):
    print("\neval inputs adience called")
    """Construct input for Adience evaluation using the Reader ops.

    Args:
        eval_data: bool, indicating if one should use the train or eval data set.

    Raises:
        ValueError: if no data_dir

    Returns:
        images: Images. 4D tensor of [batch_size, IMAGE_SIZE, IMAGE_SIZE, 3] size.
        labels: Labels. 1D tensor of [batch_size] size.
    """

    global NUM_EXAMPLES_PER_EPOCH_FOR_EVAL
    NUM_EXAMPLES_PER_EPOCH_FOR_EVAL = len(ad_input.eval_string_que)

    #TODO:
    # if not eval_data:
    #     filenames = [os.path.join(FLAGS.data_dir, 'cifar-10-batches-bin',
    #                                                         'data_batch_%d.bin' % i)
    #                              for i in xrange(1, 5)]
    #     num_examples_per_epoch = NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN
    # else:
    #     filenames = [os.path.join(FLAGS.data_dir, 'cifar-10-batches-bin',
    #                                                         'test_batch.bin')]
    #     num_examples_per_epoch = NUM_EXAMPLES_PER_EPOCH_FOR_EVAL
    #
    # for f in filenames:
    #     if not gfile.Exists(f):
    #         raise ValueError('Failed to find file: ' + f)

    # Create a queue that produces the filenames to read.
    #filename_queue = tf.train.string_input_producer(filenames)

    # Read examples from files in the filename queue.
    read_input = ad_input.read_adience_eval()
    reshaped_image = tf.cast(read_input.dec_image, tf.float32)
    print("reshaped image eval")

    height = IMAGE_SIZE
    width = IMAGE_SIZE

    # Image processing for evaluation.
    # Crop the central [height, width] of the image.
    resized_image = tf.image.resize_image_with_crop_or_pad(reshaped_image, width, height)
    print("image resized")

    # Subtract off the mean and divide by the variance of the pixels.
    float_image = tf.image.per_image_whitening(resized_image)

    # Ensure that the random shuffling has good mixing properties.
    min_fraction_of_examples_in_queue = 0.4
    min_queue_examples = int(NUM_EXAMPLES_PER_EPOCH_FOR_EVAL *
                             min_fraction_of_examples_in_queue)

    print("eval inputs adience done")

    # Generate a batch of images and labels by building up a queue of examples.
    return _generate_image_and_label_batch(float_image, read_input.label,
                                             min_queue_examples)

def inference(images):
    """Build the CIFAR-10 model.

    Args:
        images: Images returned from distorted_inputs() or inputs().

    Returns:
        Logits.
    """
    # We instantiate all variables using tf.get_variable() instead of
    # tf.Variable() in order to share variables across multiple GPU training runs.
    # If we only ran this model on a single GPU, we could simplify this function
    # by replacing all instances of tf.get_variable() with tf.Variable().
    #
    # conv1
    with tf.variable_scope('conv1') as scope:
        kernel = _variable_with_weight_decay('weights', shape=[5, 5, 3, 64],
                                                                                stddev=1e-4, wd=0.0)
        conv = tf.nn.conv2d(images, kernel, [1, 1, 1, 1], padding='SAME')
        biases = _variable_on_cpu('biases', [64], tf.constant_initializer(0.0))
        bias = tf.nn.bias_add(conv, biases)
        conv1 = tf.nn.relu(bias, name=scope.name)
        _activation_summary(conv1)

    # pool1
    pool1 = tf.nn.max_pool(conv1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
                                                 padding='SAME', name='pool1')
    # norm1
    norm1 = tf.nn.lrn(pool1, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75,
                                        name='norm1')

    # conv2
    with tf.variable_scope('conv2') as scope:
        kernel = _variable_with_weight_decay('weights', shape=[5, 5, 64, 64],
                                                                                stddev=1e-4, wd=0.0)
        conv = tf.nn.conv2d(norm1, kernel, [1, 1, 1, 1], padding='SAME')
        biases = _variable_on_cpu('biases', [64], tf.constant_initializer(0.1))
        bias = tf.nn.bias_add(conv, biases)
        conv2 = tf.nn.relu(bias, name=scope.name)
        _activation_summary(conv2)

    # norm2
    norm2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75,
                                        name='norm2')
    # pool2
    pool2 = tf.nn.max_pool(norm2, ksize=[1, 3, 3, 1],
                                                 strides=[1, 2, 2, 1], padding='SAME', name='pool2')

    # local3
    with tf.variable_scope('local3') as scope:
        # Move everything into depth so we can perform a single matrix multiply.
        dim = 1
        for d in pool2.get_shape()[1:].as_list():
            dim *= d
        reshape = tf.reshape(pool2, [FLAGS.batch_size, dim])

        weights = _variable_with_weight_decay('weights', shape=[dim, 384],
                                                                                stddev=0.04, wd=0.004)
        biases = _variable_on_cpu('biases', [384], tf.constant_initializer(0.1))
        local3 = tf.nn.relu_layer(reshape, weights, biases, name=scope.name)
        _activation_summary(local3)

    # local4
    with tf.variable_scope('local4') as scope:
        weights = _variable_with_weight_decay('weights', shape=[384, 192],
                                                                                stddev=0.04, wd=0.004)
        biases = _variable_on_cpu('biases', [192], tf.constant_initializer(0.1))
        local4 = tf.nn.relu_layer(local3, weights, biases, name=scope.name)
        _activation_summary(local4)

    # softmax, i.e. softmax(WX + b)
    with tf.variable_scope('softmax_linear') as scope:
        weights = _variable_with_weight_decay('weights', [192, NUM_CLASSES],
                                                                                stddev=1/192.0, wd=0.0)
        biases = _variable_on_cpu('biases', [NUM_CLASSES],
                                                            tf.constant_initializer(0.0))
        softmax_linear = tf.nn.xw_plus_b(local4, weights, biases, name=scope.name)
        _activation_summary(softmax_linear)

    return softmax_linear

def loss(logits, labels):
    """Add L2Loss to all the trainable variables.

    Add summary for for "Loss" and "Loss/avg".
    Args:
        logits: Logits from inference().
        labels: Labels from distorted_inputs or inputs(). 1-D tensor
                        of shape [batch_size]

    Returns:
        Loss tensor of type float.
    """
    # Reshape the labels into a dense Tensor of
    # shape [batch_size, NUM_CLASSES].
    sparse_labels = tf.reshape(labels, [FLAGS.batch_size, 1])
    #(FLAGS.batch_size, 1) if old tensorflow
    indices = tf.reshape(tf.range(0,FLAGS.batch_size,1), [FLAGS.batch_size, 1])
    concated = tf.concat(1, [indices, sparse_labels])
    dense_labels = tf.sparse_to_dense(concated,
                                        [FLAGS.batch_size, NUM_CLASSES],
                                        1.0, 0.0)

    # Calculate the average cross entropy loss across the batch.
    cross_entropy = tf.nn.softmax_cross_entropy_with_logits(
            logits, dense_labels, name='cross_entropy_per_example')
    cross_entropy_mean = tf.reduce_mean(cross_entropy, name='cross_entropy')
    tf.add_to_collection('losses', cross_entropy_mean)

    # The total loss is defined as the cross entropy loss plus all of the weight
    # decay terms (L2 loss).
    return tf.add_n(tf.get_collection('losses'), name='total_loss')

def _add_loss_summaries(total_loss):
    """Add summaries for losses in CIFAR-10 model.

    Generates moving average for all losses and associated summaries for
    visualizing the performance of the network.

    Args:
        total_loss: Total loss from loss().
    Returns:
        loss_averages_op: op for generating moving averages of losses.
    """
    # Compute the moving average of all individual losses and the total loss.
    loss_averages = tf.train.ExponentialMovingAverage(0.9, name='avg')
    losses = tf.get_collection('losses')
    loss_averages_op = loss_averages.apply(losses + [total_loss])

    # Attach a scalar summmary to all individual losses and the total loss; do the
    # same for the averaged version of the losses.
    for l in losses + [total_loss]:
        # Name each loss as '(raw)' and name the moving average version of the loss
        # as the original loss name.
        tf.scalar_summary(l.op.name +' (raw)', l)
        tf.scalar_summary(l.op.name, loss_averages.average(l))

    return loss_averages_op

def train(total_loss, global_step):
    """Train CIFAR-10 model.

    Create an optimizer and apply to all trainable variables. Add moving
    average for all trainable variables.

    Args:
        total_loss: Total loss from loss().
        global_step: Integer Variable counting the number of training steps
            processed.
    Returns:
        train_op: op for training.
    """
    # Variables that affect learning rate.
    num_batches_per_epoch = NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN / FLAGS.batch_size
    decay_steps = int(num_batches_per_epoch * NUM_EPOCHS_PER_DECAY)

    # Decay the learning rate exponentially based on the number of steps.
    lr = tf.train.exponential_decay(INITIAL_LEARNING_RATE,
                                    global_step,
                                    decay_steps,
                                    LEARNING_RATE_DECAY_FACTOR,
                                    staircase=True)
    tf.scalar_summary('learning_rate', lr)

    # Generate moving averages of all losses and associated summaries.
    loss_averages_op = _add_loss_summaries(total_loss)

    # Compute gradients.
    with tf.control_dependencies([loss_averages_op]):
        opt = tf.train.GradientDescentOptimizer(lr)
        grads = opt.compute_gradients(total_loss)

    # Apply gradients.
    apply_gradient_op = opt.apply_gradients(grads, global_step=global_step)

    # Add histograms for trainable variables.
    for var in tf.trainable_variables():
        tf.histogram_summary(var.op.name, var)

    # Add histograms for gradients.
    for grad, var in grads:
        if grad:
            tf.histogram_summary(var.op.name + '/gradients', grad)

    # Track the moving averages of all trainable variables.
    variable_averages = tf.train.ExponentialMovingAverage(
            MOVING_AVERAGE_DECAY, global_step)
    variables_averages_op = variable_averages.apply(tf.trainable_variables())

    with tf.control_dependencies([apply_gradient_op, variables_averages_op]):
        train_op = tf.no_op(name='train')

    return train_op


GhostDoc in "Windows Developer Power Tools"
Today I received my copy of "Windows Developer Power Tools" by James Avery and Jim Holmes, to which I contributed a chapter about my Visual Studio add-in GhostDoc, comparable in scope to what I wrote for "Visual Studio Hacks". For the chapter in "Tools" (which grew to 7 pages compared to 5 in "Hacks"), I took the opportunity to be more clear about what GhostDoc can do, and what it cannot achieve, and what to look out for e.g. when re-using inherited documentation.
A small error was introduced in editing of my original text (which by the way turned out surprisingly tame considering the fact that English is not my native language): Unlike suggested by the text on page 314 "when you want to document a method or class", GhostDoc does not generate documentation for classes. On a sidenote: While this is a feature that has been requested a couple of times, it hasn't been implemented yet. And honestly, I'm not quite sure of the value of such a feature, let alone what exactly it is supposed to do (most of the examples I have received so far show a certain ELIZA effect concerning the belief in GhostDoc's abilities, but I'm still open to suggestions).
To bring the topic back to the book: at more than 1300 pages, it contains a lot of stuff, covering more than 170 free and open source tools for developers. The concept of the book is to describe each tool on roughly 3 to 10 pages, helping you to decide whether or not to give it a shot, and if so, how and where to start. If "Windows Developer Power Tools" saves you from either missing a really good tool, or installing some crud software in the process of finding one, the book has reached its goal and saved you a lot of time (and maybe even trouble). As one reviewer puts it "You could spend your time tracking down these applications, but why?". From what I can tell by a first quick glance at the table of contents and skimming the pages, definitely an interesting book.
Today, exactly ten years ago, I started working at Comma Soft here in Bonn, Germany, coming straight out of university. Just amazing how fast time has gone by. Ten years at the same company is even more amazing considering the fact that I originally had planned to stay maybe one or two years, just enough to gain some professional experience, and then move on.
Update: The story "How I got Hired Straight out of University" is now online.
Monday, January 6th, 1997: This was the first day of the new year back at the Institute of Physics at Bonn University, and at the same time I knew it was about to be one of my last days. The past 14 months I had worked on my diploma thesis, which involved developing a C++ library for accessing data acquisition and motor controller modules for various bus systems. I had already turned in the thesis paper and given a talk about the project back in December, but had to wait for the results. In the meantime I was wrapping up loose ends in the code base, getting ready to move out the office, and surfing the web for a job as a software developer.
While I did have a (somewhat vague) job offer at a company I had worked for as a student, the actual job interview (more or less pro forma I was assured) had been delayed over and over again as an important meeting deciding on how many people should be added to the company hadn't taken place. It was now scheduled for end of January ("this time for sure").
After some time in my office I thought a hot chocolate would be nice. I went to the vending machine in the hallway, where I ran into one of the two professors who had read and evaluated my thesis paper. The professor told me he liked my work and asked me about my plans after university. When I answered that I was looking for a job as a software developer, he said he knew some people at a local company and could make a contact. Of course I accepted his offer, but having been promised many things in my life before (with only a fraction actually coming true) I remained a bit sceptical. It was quite a surprise when only 15 minutes later I received an email from a person at Comma Soft, asking me to call him on the phone. I called and we talked a bit about my work. After about 10 or 15 minutes he asked me if I'd like to come over for a job interview – the next day!
Interviewer: "Ok, let's see... Well, we could ask you about X…"
Interviewer: "…or we could ask you about Y"
Interviewer: "But these are things that we assume you either know…"
Interviewer: "…and if you don't, you could look them up."
Interviewer: "So here's a question that may be a bit unfair, I'm not sure, just to get a picture of you, not necessarily a problem if you can't answer that…"
Interviewer: "Do you have a rough idea what a C++ compiler does with virtual methods behind the scenes, i.e. how they are translated?"
Tons of rocks dropped from my heart. What a coincidence.
When I started my diploma thesis, I had to debunk the myth that C++ was automatically much slower than C, otherwise I wouldn't have been allowed to use something so "advanced" as C++. Among other things, I had to do perform some benchmarks on the various possible types of C++ function calls (standalone functions, class member methods, and of course virtual methods), compared to C functions calls (standard call, via a function pointer, via a pointer to a function pointer). I included the benchmark for the call via a pointer to a function pointer reasoning that if I had to reproduce in C what I could do with a virtual function call in C++, pointers to function pointers would be something I'd use. Not surprisingly, the times measured were comparable.
When I started mentioning that fact, the interviewer cut my answer short and said "yeah, there's this vtable thing, and then these function pointers and stuff, … I guess we stop here, thank you". The two devs left the room, a couple of minutes later the door opened and somebody else came in, turning out to be the person I had talked to on the phone. When he started a monologue about what a great company Comma Soft is, I knew they wanted me. But we didn't close a deal yet, instead we agreed that I would send them my final results as soon as I got them from university and that I would hear from them.
Wednesday, January, 8th: In the morning I received my final results (sooner than expected), and faxed them over to Comma Soft. A couple of hours later I received a call from Comma Soft and was asked how much money I wanted. I told the person what the other company was offering, he said that it sounded reasonable and asked me if I would like to come over the following Monday to sign a contract.
Monday, January, 13th: With the contract ready to be signed, one final question remained: When would be my first day at Comma Soft? I told them I would like to start as quick as possible. At that time I didn't have money for a journey and my idea of fun was spending my spare time learning Windows programming (prior to that, I had developed in private mostly DOS, and the diploma thesis was for *ix systems). They said "well, we don't have a computer ready for you by tomorrow, but what about Wednesday?". I agreed and signed the contract.
Wednesday, January, 15th 1997: My first day of work at Comma Soft. Funny, originally I had planned to choose my first company very carefully - and here I was sitting at a desk just nine days after my first contact with an unknown company! On the other hand, the people I met and my overall impression of the company seemed nice enough to convince me. I reckoned that I could start looking for another company if things wouldn't work out, making some money and gaining some experience in the mean time.
P.S. I received an email from the other company at the end of January. Now they were ready for a job interview - but I wasn't anymore.
Need a New Year's Resolution? What about User Groups?
You are a developer by passion. You are interested in new technologies, even in your spare time. You are driven to keep your skills up-to-date. You are reading blogs, maybe you are writing a blog yourself.
The developers around you are different, and you can't even blame them. Maybe they are busy building a house, their children are keeping them on their toes, a non-computer hobby eats away their spare time – or they are simply working crazy hours and need a well-deserved rest when they get home.
You would like to get in touch with other developers. The Internet offers a huge number of possibilities to get connected to people all over the world, but let's be honest, nothing beats meeting "real" people.
Have you thought about joining a .NET user group?
At a .NET user group, you'll find a wide variety of people, coming from different backgrounds, with vastly different skills and experience levels, all united by the common interest in .NET technologies. Most user groups are having regular (e.g. monthly) meetings, where talks are given by members or external speakers – this is were you meet real people, not online personae.
A good place to find out whether a user group exists in your area is the website of the International .NET Association (INETA) at www.ineta.org. Choose your region and start looking for a nearby user group. If you find a group in your area, things are really easy: go to their website, get in contact and visit one of their meetings.
If you are wondering whether you are a "user group person": Don't be shy or too critical of your own skills – a user group meeting is not a place where you have to prove yourself in any way. If you prefer sitting quietly in the back, you can do just that. Nobody will force you to become a speaker the minute you enter the room.
Don't be late (yes, duh). Note that coming late can be a major annoyance, e.g. if the meeting takes place in a company meeting room, where somebody may have to pick you up at the entrance.
Come a bit earlier so you can talk to the user group leader or the person you had contact with, or other members. Don't be way too early as that will most likely disturb the preparations (setting up equipment etc). The best thing is to ask your contact for when other members usually start to show up.
Prepare a short introduction you can use when talking to other people – who are you, what are you doing, what are your interests. You don't want to drive home thinking "dang, I should have mentioned I'm very interested in technology X" given the chance that somebody else could have started a conversation "hey, I'm using X, too… what tools are you working with?".
Avoid being under time pressure. Depending on the user group and their meeting location, group members may go to a bar or a restaurant after the "official part" of the meeting. Plan for enough time to join them, it's the best thing you can do to get in contact with other people.
But what if the next user group is too far away?
Have you thought about founding a .NET user group?
So you didn't find a user group near where you live. Of course you could wait until a group is founded by somebody else. That's what I did for almost a year before I eventually founded a group myself in Bonn, Germany called Bonn-to-Code.Net (yup, pun intended, all credits go to my buddy Jens Schaller) – that was exactly one year ago on January 1st, 2006.
For a start, keep it simple. Don't dream up what could be done with forums, wikis and blogs; you'd need a critical mass that's hard to reach on the Internet, let alone in the context of a local user group.
It's all about content. It doesn't matter if you start with a website consisting of a single page whipped up in FrontPage if it's updated on a regular basis – even if it's just a blog-style report describing the process of founding a user group. Long-term solutions can be searched for after the first meeting(s).
Remember that user groups are about people. Too many user group websites make it hard to see that. Who are you? What are your interests?
Don't try to "sell" too hard. Yes, there are benefits for the members of a user group (e.g. rebates on software licenses and/or books, see the INETA website for more on that), but that shouldn't be the focus. You are not looking for customers you have to convince, you are looking for a bunch of devs coming together and having some fun.
Use everything at your disposal: your blog (if you have one), community websites like codezone.com/de, etc. Create flyers, posters – whatever comes to your mind.
On the website you'll find information on how to start a .NET user group, how to apply for (free) membership, etc. Don't expect miracles from INETA, but founding a user group and not contacting INETA is plain dumb.
In Bonn, the first meeting consisted mainly of short introductions on two or three PowerPoint slides. Obviously, this approach doesn't scale well beyond let's say 15 people, but looking back this really gave the group a jump start in terms getting to know each other. The evening did not offer any technical content, but dealt with planning future meetings, deciding on topics, etc.
In Cologne, the first meeting had a well-known conference speaker giving a talk. There was no time for really speaking about organizational issues, and after the event, the speaker was the focus of attention. Nevertheless, the Cologne group is now running successfully. Starting "big" attracted people who otherwise wouldn't have shown up (by their own honest account), many of them still being members.
A third approach would to split the time between a talk (given by a speaker that will be a member of the user group) and an organisational part of the evening. People could introduce themselves without support by PowerPoint slides, but that tends to less "sticky" than text and speech.
Make sure you have a rough idea for the second meeting. Maybe you are lucky to you have a bunch of "natural born speakers" in your group, eager to give talks. If that is not the case (or if you don't find enough material for a full evening), have a plan B up your sleeve, i.e. a talk that you could give. Note that your primary goal must be to get other people involved as quickly as possible, which brings us to the next point.
Make it easy to author PowerPoint slides that "fit in", i.e. have a certain "official user group feel" for those that are not comfortable developing their own design or using a stock template shouting "look at me, I'm a PowerPoint n00b".
Don't require each talk to be of same length and depth. Sometimes just a hint on a single hotkey, a small tool or a cool website can be enough for the visitors of the meeting to drive back home with a positive feeling. At Bonn-to-Code.Net, a "QuickTip" consists of just a few slides: what's the problem, what's the solution, and a short demo (sometimes maybe even just a screenshot).
There are so many hidden talents out there that could be speakers for you user group, you only have to find them. The most important thing is to get them speaking for the first time – it's usually less of a problem to convince them to give a second and a third talk.
New year's resolutions are about changing something in your life. If you get involved in a user group, you life will change – be it just a little bit (just coming to meet other developers), a little bit more (taking the plunge and contributing a talk here and there), or a whole lot more (founding and running a user group). Whatever you do, be assured you'll have lots of fun.


Our little miss Harlow Renee turned 3 today. She is growing and changing so much it makes me, quite frankly, happy. I know, I know! Everyone always says not to rush their "babyhood", but honestly Harlow was a bit of a tough cookie through her two's -- being very independent, defiant, mischievous and dangerously overconfident. Although these traits shined through, she has additionally been very affectionate and still absolutely is. She loves curling up into our arms, giving us hugs or kisses and constantly saying "I love you so much". Such a fierce little girl with an incredibly sweet side. I've noticed over the last month or so a calmer more disciplined part of her. I'm not sure if she is just maturing or caving into finally accepting rules and limits, but I'm liking it.. a lot. I enjoy her sweet little company and she is absolutely one of my most favorite people (along with Brody and Jess).
Here are some of my very favorite details from her special day!
Are you dying over this cake, because I am. Harlow's cake was utterly amazing. I rave about Lila all the time, but seriously she nailed my idea beyond expectation. You can find her over on Instagram or Facebook and you should really follow her, like now! lol I promise she won't disappoint. She will be creating Brody's cake too and I'm honestly so excited. I actually had a tiny bit of anxiety cutting into this cake today because it was literally almost too pretty to eat!
The morning was spent enjoying "magical" peace, love and little donuts.. followed by dance and then an evening with her family opening presents. We ordered pizza, had a few snacks and listened to Pandora (while mommy and daddy might of enjoyed a beer or two lol). It was small, it was perfect and Harlow was happy -- and really, that's all that matters.
Happy 3rd Birthday to our little love.
You can check out Harlow's boho-themed birthday over here.


text:
Henry Hinchliffe Ainley, född 21 augusti 1879 och död 31 oktober 1945, var en engelsk skådespelare.

Biografi
Ainley, som från 1900 varit anställd vid olika teatrar i London, ansågs av Gunnar Klintberg som en av Englands högst siktande och finast stämda skådespelare. Bland hans roller märks Orestes i Elektra, och Hippolytos (Euripides), Leontes i En vintersaga, Prospero i Stormen, Marcus Antonius i Julius Cæsar, Shylock i Köpmannen i Venedig, Faust, Nils Lykke i Fru Inger till Östråt och Oliver Cromwell (Drinkwater).

Källor
Svensk uppslagsbok, Lund 1929

Födda 1879
Brittiska skådespelare
Avlidna 1945
Män

redpajama_set_name: RedPajamaWikipedia