Computation
interface and which describes the compute
method. Calculations in Giraph are super superstep. At each super-step for the vertices, the compute method is called. Between supersteps vertices are delivered messages from other vertices, sent during the previous superstep. Messages have their own type, which does not have to match the types of vertices or edges.DefaultMaster
is used in calculations - a class inherited from the abstract MasterCompute
class. By itself, it is rather useless, since it does nothing (in many tasks nothing is required of it). Fortunately, there is an opportunity to make your own master, it must be inherited from MasterCompute
, and the compute
method must be implemented in it, which is performed before each super-step. The wizard allows you to select a class for the vertices, aggregate some data from the vertices and complete the calculations.voteToHalt()
method is called inside the compute
, then at the end of the super step, the vertex “falls asleep” - it becomes inactive at the next super step. Between supersteps vertices are delivered messages sent by other vertices in the course of superstep. All vertices that did not fall asleep at the previous super-step or received messages before the next are active and participate in the new super-step, performing their compute method. If all vertices are inactive, then the calculations end. At the end of the super step, the current state of the graph is saved to disk.WorkerContext
is implemented - a class in which the current state of the network is calculated and from which the vertices can get the context of the running application. Thirdly, these are the parameters passed at startup. The master and context receive the values ​​passed in the parameters.maxEpochNumber
- the number of training repetitions on all data;maxBatchNumber
- the number of batches used for training;maxStepInSample
- the number of runs of the batch from the visible layer to the hidden and back;learningRate
- the pace of learning;visibleLayerSize
- the number of neurons on the visible layer;hiddenLayerSize
- the number of neurons on the hidden layer;inputPath
- the path to the input files;useSampling
- if true, performs sampling on a hidden layer.VisibleInitialize
class. Then the same thing happens for the hidden layer with the class HiddenInitialize
. Once the network is created, you can start learning. It consists of several eras. The epoch, in turn, is a consistent learning on this set of batches. Thus, in order to understand how the whole learning process takes place, it is necessary to understand learning in one batch.maxStepInSample
- 3) times.(maxStepInSample + 1) * 2
steps.maxStepInSample
= 1), the scheme changes somewhat (scheme 2):Class name | Purpose | Relevant Steps | |
From scheme 1 | From scheme 2 | ||
VisibleNeuronZero | The zero stage of learning with reading data also preserves the positive part of the gradient for offsets | one | one |
VisibleNeuronFirst | Preserves the positive part of the gradient in the visible layer | 3 | - |
VisibleNeuronLast | The last step of the training on the visible layer, considers the negative part of the gradient and updates the weights of the edges emerging from the visible layer. | eight | - |
VisibleNeuronOneStep | The combination of classes VisibleNeuronFirst and VisibleNeuronLast , is used in the case when maxStepInSample = 1 | - | 3 |
HiddenNeuronFirst | Saves a positive gradient value for the hidden layer (only for offsets b , weights gradients w are calculated on the visible layer) | 2 | 2 |
HiddenNeuronLast | Updates the weights of ribs emerging from the hidden layer. | 9 | four |
NeuronDefault | Called both on the visible layer and on the hidden layer and simply recalculates the activation values. | 4, 5, 6 | - |
compute
from InitialNode
. The only thing that happens in this class is sending out empty messages to vertices with id from 1 to visibleLayerSize
(vertices with these identifiers are immediately created and used in the next step). Immediately after this, the vertex goes to sleep (a voteToHalt call) and is never used again.VisibleInitialize
as the compute class, so the vertices created as a result of sending the message will execute it. First, the vertex generates edges going out of it with random weights to the vertices with the id from –hiddenLayerSize
to –1. Then these weights are sent to the corresponding vertices of the hidden layer (recall that this causes the creation of these vertices). And at the end of each vertex a zero offset is added. After that, the visible layer "falls asleep", since its creation is complete.HiddenInitialize
is executed in the neurons of the hidden layer. Each vertex is iterated by the messages that came to it and creates the corresponding edges. Thus, each connection between neurons is defined by two edges with the same weights. Next, create a zero offset. Then the hidden layer “wakes up” visible with empty messages, and “falls asleep” itself.NeuronCompute
. All classes of neurons are inherited from it, and compute
functions are implemented in them, in which both NeuronCompute
methods and methods specific to a particular class can be called. And here, actually, and the list of the functions realized in it:sigma
- returns the value of the sigma function from the passed parameter;ComputeActivateValues
- returns the activation values ​​for the batch according to the activation values ​​received in the messages and the vertex offset;SendActivateValues
- sends activation values ​​to outgoing edges;ComputePositivePartOfBiasGradient
- returns the positive part of the gradient for offsets;UpdateBiases
- updates the offset;AddBias
- adds zero offset to the vertex;SendNewEdges
- sends new edge values ​​to the hidden layer.ReadBatch
and compute
. It is easy to guess that in the ReadBatch
method the next batch is loaded. The number of the batch and the path are taken from the WorkerContext. /** * */ double[] activateValues; double positiveBias; activateValues = ReadBatch(vertex); // . WorkerContext' positiveBias = ComputePositivePartOfBiasGradient(activateValues); // SendActivateValues(vertex, activateValues); // /** * */ JSONWritable value = vertex.getValue(); value.addDouble("Positive bias", positiveBias); value.addArray("Value", activateValues); vertex.setValue(value); vertex.voteToHalt(); //
NeuronCompute
class. Take a look at his compute
: /** * */ double bias = vertex.getValue().getDouble("Bias"); double[] activateValues; double positiveBias; activateValues = ComputeActivateValues(vertex, messages, bias); // positiveBias = ComputePositivePartOfBiasGradient(activateValues); // SendActivateValues(vertex, activateValues); // /** * */ JSONWritable value = vertex.getValue(); value.addDouble("Positive bias", positiveBias); value.addArray("Value", activateValues); vertex.setValue(value); vertex.voteToHalt(); //
/** * */ double bias = vertex.getValue().getDouble("Bias"); double[] activateValues = vertex.getValue().getArray("Value"); HashMap<Long, Double> positiveGradient; ArrayList valueAndGrad = ComputeActivateValuesAndPositivePartOfGradient(vertex, messages, activateValues, bias); // activateValues = (double[])valueAndGrad.get(0); positiveGradient = (HashMap<Long, Double>)valueAndGrad.get(1); SendActivateValues(vertex, activateValues); // /** * */ JSONWritable value = vertex.getValue(); value.addArray("Value", activateValues); value.addHashMap("Positive gradient", positiveGradient); vertex.setValue(value); vertex.voteToHalt(); //
/** * */ double bias = vertex.getValue().getDouble("Bias"); double[] activateValues; activateValues = ComputeActivateValues(vertex, messages, bias); // SendActivateValues(vertex, activateValues); // /** * */ JSONWritable value = vertex.getValue(); value.addArray("Value", activateValues); vertex.setValue(value); vertex.voteToHalt(); //
/** * */ double bias = vertex.getValue().getDouble("Bias"); double[] activateValues = vertex.getValue().getArray("Value"); double positiveBias = vertex.getValue().getDouble("Positive bias"); activateValues = ComputeGradientAndUpdateEdges(vertex, messages, activateValues.length, bias); // . bias = UpdateBiases(activateValues, bias, positiveBias); // SendNewEdges(vertex); // /** * */ JSONWritable value = vertex.getValue(); value.addArray("Value", activateValues); value.addDouble("Bias", bias); vertex.setValue(value); vertex.voteToHalt(); //
/** * */ double bias = vertex.getValue().getDouble("Bias"); double[] activateValues = vertex.getValue().getArray("Value"); double positiveBias = vertex.getValue().getDouble("Positive bias"); UpdateEdgesByMessages(vertex, messages); // bias = UpdateBiases(activateValues, bias, positiveBias); // /** * */ JSONWritable value = vertex.getValue(); value.addDouble("Bias", bias); vertex.setValue(value); vertex.voteToHalt(); //
/** * */ double bias = vertex.getValue().getDouble("Bias"); double[] activateValues = vertex.getValue().getArray("Value"); double positiveBias = vertex.getValue().getDouble("Positive bias"); activateValues = ComputeGradientAndUpdateEdges(vertex, messages, activateValues, bias); // bias = UpdateBiases(activateValues, bias, positiveBias); // SendNewEdges(vertex); // /** * */ JSONWritable value = vertex.getValue(); value.addArray("Value", activateValues); value.addDouble("Bias", bias); vertex.setValue(value); vertex.voteToHalt(); //
JSONWritable
- the class that we use as a class for the value at the vertices and for the messages to be sent. In essence, this is a Writable wrapper for a JSON object. This data storage format is convenient because the type and number of values ​​that need to be stored vary depending on the current iteration. Google GSON is used as a library for working with JSON. For recording and sending, the JSON string is wrapped in an object of the Text class.readFieds
— read an object of this class from the input stream and write
— write the object to the output stream. Below is their implementation for the JSONWritable
class. public class JSONWritable implements Writable { private JsonElement json; public void write(DataOutput out) throws IOException { Text text = new Text(this.json.toString()); text.write(out); } public void readFields(DataInput in) throws IOException { Text text = new Text(); text.readFields(in); JsonParser parser = new JsonParser(); this.json = parser.parse(text.toString()); } /* */ }
JSONWritable
has written special methods for adding and extracting a HashMap by key. public HashMap getHashMap(String key) { HashMap a = new HashMap(); Iterator it = this.json.getAsJsonObject().getAsJsonObject(key).entrySet().iterator(); while (it.hasNext()) { Map.Entry me = (Map.Entry) it.next(); a.put(Long.valueOf(me.getKey().toString()), ((JsonElement) me.getValue()).getAsDouble()); } return a; } public void addHashMap(String key, HashMap a) { Iterator it = a.entrySet().iterator(); JsonObject obj = new JsonObject(); while (it.hasNext()) { Map.Entry me = (Map.Entry) it.next(); obj.addProperty(me.getKey().toString(), (double) me.getValue()); } this.json.getAsJsonObject().add(key, obj); }
[Id_, __, _]
, where Id
is an integer,
is a JSON-object, the
is a JSON-list of the form [[Id__, __], … ]
.-ca giraph.outEdgesClass=org.apache.giraph.edge.ArrayListEdges
( ).maxStepInSample=2
), , 6 . . , , , , ( 2—4), 20%.Source: https://habr.com/ru/post/306362/
All Articles